วิธีเปรียบเทียบสตริงใน C เงื่อนไขคำสั่งพรีโปรเซสเซอร์


92

ฉันต้องทำสิ่งนี้ใน C มันใช้ได้ก็ต่อเมื่อฉันใช้ถ่าน แต่ฉันต้องการสตริง ฉันจะทำเช่นนี้ได้อย่างไร?

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif

ทำไมคุณถึงใช้ strcmp ไม่ได้?

@ ไบรอัน: ใช่ฉันอ่านคำถามด้วย :-) แค่อยากให้แน่ใจว่าเขารู้ว่ามี strcmp อยู่และการตอบสนองอาจทำให้กระจ่างได้เพราะฉันไม่สามารถคิดเหตุผลที่จะทำสิ่ง #define นี้ได้

2
แค่อยากจะบอกว่าสิ่งเดียวกันนั้นก็ใช้กับรหัสปกติเช่นกันไม่ใช่แค่ตัวประมวลผลล่วงหน้า อย่าใช้สตริงเมื่อค่าธรรมดาจะทำ สตริงมีค่าโสหุ้ยมากกว่าจำนวนเต็มหรือ enum มากและถ้าคุณไม่จำเป็นต้องทำอะไรมากไปกว่าการเปรียบเทียบสตริงก็เป็นทางออกที่ผิด
swestrup

จะเป็นประโยชน์หากคำถามมีข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับพฤติกรรมที่ต้องการเทียบกับพฤติกรรมจริง
Brent Bradburn

คำตอบ:


71

ฉันไม่คิดว่าจะมีวิธีเปรียบเทียบสตริงความยาวตัวแปรได้อย่างสมบูรณ์ในคำสั่งพรีโปรเซสเซอร์ คุณอาจทำสิ่งต่อไปนี้ได้:

#define USER_JACK 1
#define USER_QUEEN 2

#define USER USER_JACK 

#if USER == USER_JACK
#define USER_VS USER_QUEEN
#elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif

หรือคุณสามารถ refactor รหัสเล็กน้อยและใช้รหัส C แทน


3
หรือ#define USER_VS (3 - USER)ในกรณีเฉพาะนี้ :)
Jesse Chisholm

17

[อัพเดท: 2018.05.03]

CAVEAT : ไม่ใช่ทุกคอมไพเลอร์ที่ใช้ข้อกำหนด C ++ 11 ในลักษณะเดียวกัน โค้ดด้านล่างใช้งานได้ในคอมไพเลอร์ที่ฉันทดสอบในขณะที่ผู้แสดงความคิดเห็นหลายคนใช้คอมไพเลอร์อื่น

อ้างจากคำตอบของ Shafik Yaghmour ที่: การคำนวณความยาวของสตริง C ในเวลาคอมไพล์ นี่คือ constexpr จริงๆหรือ?

ไม่รับประกันว่านิพจน์คงที่จะได้รับการประเมินในเวลาคอมไพล์เรามีเพียงคำพูดที่ไม่ใช่บรรทัดฐานจากร่างมาตรฐาน C ++ ส่วน 5.19 นิพจน์คงที่ซึ่งระบุว่าสิ่งนี้แม้ว่า:

[... ]> [หมายเหตุ: นิพจน์คงที่สามารถประเมินได้ระหว่างการแปล - หมายเหตุตอนท้าย]

คำนั้นcanสร้างความแตกต่างในโลก

ดังนั้น YMMV สำหรับคำตอบนี้ (หรือใด ๆ ) ที่เกี่ยวข้องconstexprขึ้นอยู่กับการตีความข้อมูลจำเพาะของผู้เขียนคอมไพเลอร์

[อัพเดท 2016.01.31]

เนื่องจากบางคนไม่ชอบคำตอบก่อนหน้านี้ของฉันเพราะมันหลีกเลี่ยงcompile time string compareแง่มุมทั้งหมดของ OP โดยการบรรลุเป้าหมายโดยไม่จำเป็นต้องมีการเปรียบเทียบสตริงนี่คือคำตอบโดยละเอียด

คุณทำไม่ได้! ไม่อยู่ใน C98 หรือ C99 ไม่ได้อยู่ใน C11 การปรับแต่ง MACRO จะไม่เปลี่ยนแปลงสิ่งนี้

คำจำกัดความของconst-expressionใช้ใน#ifไม่อนุญาตสตริง

อนุญาตให้ใช้อักขระดังนั้นหากคุณ จำกัด ตัวเองเป็นอักขระคุณอาจใช้สิ่งนี้:

#define JACK 'J'
#define QUEEN 'Q'

#define CHOICE JACK     // or QUEEN, your choice

#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

คุณสามารถ! ใน C ++ 11. หากคุณกำหนดฟังก์ชันตัวช่วยเวลาคอมไพล์สำหรับการเปรียบเทียบ

// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
    return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
        :  (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
        : c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])

#define JACK "jack"
#define QUEEN "queen"

#define USER JACK       // or QUEEN, your choice

#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif

#pragma message "USER    IS " USER
#pragma message "USER_VS IS " USER_VS

ดังนั้นในที่สุดคุณจะต้องเปลี่ยนวิธีที่คุณ accomlish เป้าหมายของคุณในการเลือกค่าสตริงสุดท้ายและUSERUSER_VS

คุณไม่สามารถทำการเปรียบเทียบสตริงเวลาใน C99 ได้ แต่คุณสามารถรวบรวมเวลาในการเลือกสตริงได้

หากคุณต้องทำการเปรียบเทียบเวลาต่อยจริงๆคุณต้องเปลี่ยนเป็น C ++ 11 หรือตัวแปรที่ใหม่กว่าที่อนุญาตให้ใช้คุณลักษณะนั้นได้

[ตามคำตอบเดิม]

ลอง:

#define jack_VS queen
#define queen_VS jack

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS

// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U

อัปเดต: การวางโทเค็น ANSI บางครั้งก็น้อยกว่าที่ชัดเจน ;-D

การใส่ซิงเกิล#ก่อนมาโครจะทำให้มาโครเปลี่ยนเป็นสตริงของค่าแทนค่าเปล่า

การใส่คู่##ระหว่างสองโทเค็นจะทำให้โทเค็นเชื่อมต่อกันเป็นโทเค็นเดียว

ดังนั้นมาโครUSER_VSจึงมีการขยายjack_VSหรือqueen_VSขึ้นอยู่กับว่าคุณตั้งค่าUSERอย่างไร

แมโครstringifyS(...)ใช้การกำหนดทิศทางแมโครดังนั้นค่าของมาโครที่ตั้งชื่อจะถูกแปลงเป็นสตริง แทนชื่อของมาโคร

ดังนั้นจึงUSER##_VSกลายเป็นjack_VS(หรือqueen_VS) ขึ้นอยู่กับว่าคุณตั้งค่าUSERอย่างไร

ต่อมาเมื่อstringifyแมโครจะถูกใช้เป็นS(USER_VS)ค่าของUSER_VS( jack_VSในตัวอย่างนี้) จะถูกส่งผ่านไปยังขั้นตอนตรงS_(jack_VS)ซึ่งจะแปลงความคุ้มค่า ( queen) "queen"เป็นสตริง

หากคุณตั้งUSERไปแล้วผลสุดท้ายคือสตริงqueen"jack"

สำหรับการต่อโทเค็นโปรดดู: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

สำหรับการแปลงสตริงโทเค็นโปรดดู: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[อัปเดต 2015.02.15 เพื่อแก้ไขการพิมพ์ผิด]


5
@JesseChisholm คุณตรวจสอบเวอร์ชัน C ++ 11 ของคุณแล้วหรือยัง? ฉันไม่สามารถทำให้มันทำงานบน GCC 4.8.1, 4.9.1, 5.3.0 มันบอกว่า {{ไม่มีตัวดำเนินการไบนารีก่อนโทเค็น "("}} เมื่อ {{#if 0 == c_strmp / * here * / (USER, QUEEN)}}
Dmitriy Elisov

3
@JesseChisholm ดังนั้นฉันจึงสามารถรวบรวมตัวอย่าง C ++ 11 ของคุณได้ถ้าฉันเปลี่ยน#if 0 == c_strcmp( USER, JACK )เป็นconstexpr int comp1 = c_strcmp( USER, JACK ); #if 0 == comp1
Dmitriy Elisov

4
@JesseChisholm อืมยังไม่มีโชค ตัวแปร constexpr ใด ๆ เท่ากับศูนย์ใน#ifตัวอย่างของคุณใช้งานได้เพราะ USER เป็น JACK เท่านั้น ถ้า USER เป็น QUEEN ก็จะพูดUSER IS QUEENและUSER_VS IS QUEEN
Dmitriy Elisov

9
c ++ 11 ส่วนนี้ของคำตอบนี้ไม่ถูกต้อง คุณไม่สามารถเรียกใช้ฟังก์ชัน (แม้constexpr) จากคำสั่งพรีโปรเซสเซอร์
interjay

8
คำตอบที่ผิดแบบแบนนี้ทำให้คนที่อ้างถึงเข้าใจผิดไปแล้ว คุณไม่สามารถเรียกใช้ฟังก์ชัน constexpr จากตัวประมวลผลล่วงหน้า constexpr ไม่ได้รับการยอมรับว่าเป็นคีย์เวิร์ดจนกว่าการแปลเฟส 7 การประมวลผลล่วงหน้าจะเสร็จสิ้นในขั้นตอนการแปล 4
H Walters

10

ต่อไปนี้ใช้ได้ผลกับฉันด้วยเสียงดัง อนุญาตให้สิ่งที่ปรากฏเป็นการเปรียบเทียบค่ามาโครเชิงสัญลักษณ์ #error xxxเป็นเพียงการดูว่าคอมไพเลอร์ทำอะไรได้บ้าง การแทนที่นิยามcatด้วย#define cat (a, b) a ## bแบ่งสิ่งต่างๆ

#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__

#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)

#define USER jack // jack or queen

#if USER_VAL==xUSER_jack
  #error USER=jack
  #define USER_VS "queen"
#elif USER_VAL==xUSER_queen
  #error USER=queen
  #define USER_VS "jack"
#endif

ไม่แน่ใจว่านี่คือความชั่วร้ายยอดเยี่ยมหรือทั้งสองอย่าง แต่มันคือสิ่งที่ฉันกำลังมองหา - ขอบคุณ! เคล็ดลับที่เป็นประโยชน์อีกอย่างหนึ่งคือ # กำหนดมาโคร xUSER_ ของคุณโดยเริ่มจาก 1 จากนั้นคุณสามารถเพิ่มประโยค #else ต่อท้ายรายการ #elsif ของคุณเพื่อตรวจจับกรณีที่ USER ตั้งค่าเป็นสิ่งที่คุณไม่ทราบวิธีจัดการโดยไม่ได้ตั้งใจ (มิฉะนั้นถ้าคุณเลขจาก 0 แล้วตัวพิมพ์ 0 จะกลายเป็น catchall ของคุณเพราะนั่นคือค่าตัวเลขเริ่มต้นของตัวประมวลผลล่วงหน้าสำหรับสัญลักษณ์ที่ไม่ได้กำหนด)
sclamage

8

ใช้ค่าตัวเลขแทนสตริง

สุดท้ายในการแปลงค่าคงที่ JACK หรือ QUEEN เป็นสตริงให้ใช้ตัวดำเนินการ stringize (และ / หรือ tokenize)


2

ตามที่ระบุไว้ข้างต้นตัวประมวลผลล่วงหน้า ISO-C11 ไม่รองรับการเปรียบเทียบสตริง อย่างไรก็ตามปัญหาในการกำหนดมาโครด้วย "ค่าตรงกันข้าม" สามารถแก้ไขได้ด้วย "การวางโทเค็น" และ "การเข้าถึงตาราง" โซลูชันมาโครเชื่อมต่อ / stringify อย่างง่ายของ Jesse ล้มเหลวด้วย gcc 5.4.0 เนื่องจากการทำให้สตริงเสร็จสิ้นก่อนการประเมินการเรียงต่อกัน (สอดคล้องกับ ISO C11) อย่างไรก็ตามสามารถแก้ไขได้:

#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U

#define jack_VS  queen
#define queen_VS jack

S (VS (jack))
S (jack)
S (VS (queen))
S (queen)

#define USER jack          // jack    or queen, your choice
#define USER_VS USER##_VS  // jack_VS or queen_VS
S (USER)
S (USER_VS)

บรรทัดแรก (มาโครP_()) จะเพิ่มทิศทางเดียวเพื่อให้บรรทัดถัดไป (มาโครVS()) เสร็จสิ้นการต่อกันก่อนที่จะสตริง (ดูเหตุใดฉันจึงต้องมีการกำหนดทิศทาง สองชั้นสำหรับมาโคร ) มาโครการสตริง ( S()และS_()) มาจาก Jesse

ตาราง (มาโครjack_VSและqueen_VS) ซึ่งง่ายต่อการบำรุงรักษามากกว่าการสร้าง OP จากเจสซี่

ในที่สุดบล็อกสี่บรรทัดถัดไปจะเรียกใช้มาโครสไตล์ฟังก์ชัน บล็อกสี่บรรทัดสุดท้ายมาจากคำตอบของเจสซี่

การจัดเก็บรหัสfoo.cและเรียกใช้ตัวประมวลgcc -nostdinc -E foo.cผลล่วงหน้าให้ผลตอบแทน:

# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"



"jack"
"USER_VS"

ผลลัพธ์เป็นไปตามที่คาดไว้ บรรทัดสุดท้ายแสดงว่าUSER_VSมาโครไม่ได้ขยายก่อนการสตริง


สิ่งนี้ใช้งานได้ดีจนกว่าฉันจะลองเปรียบเทียบสตริงที่สร้างขึ้นจริงเพื่อทำการคอมไพล์ตามเงื่อนไข: #if (S(USER)=="jack")- ฉันได้รับข้อผิดพลาดของตัวประมวลผลล่วงหน้าเมื่อใช้"- error: invalid token at start of a preprocessor expression.
ysap

1

หากสตริงของคุณรวบรวมค่าคงที่ของเวลา (เช่นในกรณีของคุณ) คุณสามารถใช้เคล็ดลับต่อไปนี้:

#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif

คอมไพลเลอร์สามารถบอกผลลัพธ์ของ strcmp ล่วงหน้าและจะแทนที่ strcmp ด้วยผลลัพธ์ดังนั้นจึงให้ #define ที่สามารถเปรียบเทียบกับคำสั่งพรีโปรเซสเซอร์ได้ ฉันไม่รู้ว่ามีความแปรปรวนระหว่างคอมไพเลอร์ / การพึ่งพาตัวเลือกคอมไพเลอร์หรือไม่ แต่มันใช้ได้กับฉันใน GCC 4.7.2

แก้ไข: จากการตรวจสอบเพิ่มเติมดูเหมือนว่านี่เป็นส่วนขยาย toolchain ไม่ใช่ส่วนขยาย GCC ดังนั้นโปรดพิจารณา ...


7
นี่ไม่ใช่มาตรฐาน C อย่างแน่นอนและฉันไม่เห็นว่ามันจะทำงานร่วมกับคอมไพเลอร์ใด ๆ บางครั้งคอมไพเลอร์สามารถบอกผลลัพธ์ของนิพจน์ได้ (แม้กระทั่งการเรียกใช้ฟังก์ชันหากอยู่ในบรรทัด) แต่ไม่ใช่ตัวประมวลผลล่วงหน้า คุณใช้งาน$ส่วนขยายก่อนโปรเซสเซอร์บางประเภทหรือไม่?
ugoren

3
ดูเหมือนว่าไวยากรณ์ '#if $ USER_JACK == 0' จะใช้งานได้อย่างน้อยกับ GNU C ++ ที่ใช้สำหรับสร้างโค้ด Android ดั้งเดิม (JNI) ... ฉันไม่ทราบสิ่งนี้ แต่มีประโยชน์มากขอบคุณที่แจ้งให้เราทราบเกี่ยวกับ มัน!
gregko

6
ฉันลองใช้กับ GCC 4.9.1 แล้วและฉันไม่เชื่อว่าสิ่งนี้จะทำได้อย่างที่คุณคิด แม้ว่าโค้ดจะคอมไพล์ แต่ก็ไม่ได้ให้ผลลัพธ์ที่คาดหวัง "$" ถือเป็นชื่อตัวแปร ดังนั้นตัวประมวลผลก่อนกำลังมองหาตัวแปร '$ USER_JACK' ไม่พบและให้ค่าเริ่มต้นเป็น 0 ดังนั้นคุณจะกำหนด USER_VS เป็น USER_QUEEN เสมอโดยไม่คำนึงถึง strcmp
Vitali

1

คำตอบของPatrickและJesse Chisholmทำให้ฉันทำสิ่งต่อไปนี้:

#define QUEEN 'Q'
#define JACK 'J'

#define CHECK_QUEEN(s) (s==QUEEN)
#define CHECK_JACK(s) (s==JACK)

#define USER 'Q'

[... later on in code ...]

#if CHECK_QUEEN(USER)
  compile_queen_func();
#elif CHECK_JACK(USER)
  compile_jack_func();
#elif
#error "unknown user"
#endif

แทน #define USER 'Q' #define USER QUEEN ควรใช้งานได้เช่นกัน แต่ไม่ได้รับการทดสอบ ยังใช้งานได้และอาจจัดการได้ง่ายกว่า

แก้ไข: ตามความคิดเห็นของ @ Jean-François Fabre ฉันปรับคำตอบของฉัน


การเปลี่ยนแปลง(s==QUEEN?1:0)โดย(s==QUEEN)คุณไม่จำเป็นต้องใช้ ternary ผลลัพธ์ก็คือบูลีนอยู่แล้ว
Jean-François Fabre

0
#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;

#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'

#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "jack"
#endif

โดยพื้นฐานแล้วอาร์เรย์ char คงที่ความยาวคงที่จะเริ่มต้นด้วยตนเองแทนที่จะเป็นอาร์เรย์ char แบบคงที่ที่มีความยาวตัวแปรเริ่มต้นโดยอัตโนมัติจะลงท้ายด้วย null ที่สิ้นสุด


0

คุณไม่สามารถทำได้หาก USER ถูกกำหนดเป็นสตริงที่ยกมา

แต่คุณสามารถทำได้ถ้า USER เป็นแค่ JACK หรือ QUEEN หรือ Joker หรืออะไรก็ตาม

มีสองเทคนิคที่จะใช้:

  1. Token-splicing ที่คุณรวมตัวระบุกับตัวระบุอื่นโดยเพียงแค่เชื่อมอักขระของพวกเขาเข้าด้วยกัน สิ่งนี้ช่วยให้คุณเปรียบเทียบกับ JACK ได้โดยไม่ต้องมี#define JACKอะไร
  2. การขยายมาโครแบบแปรผันซึ่งช่วยให้คุณจัดการมาโครที่มีอาร์กิวเมนต์จำนวนตัวแปรได้ วิธีนี้ช่วยให้คุณสามารถขยายตัวระบุเฉพาะเป็นจำนวนจุลภาคที่แตกต่างกันซึ่งจะเป็นการเปรียบเทียบสตริงของคุณ

เริ่มจาก:

#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)

ตอนนี้ถ้าฉันเขียนJACK_QUEEN_OTHER(USER)และ USER คือ JACK ตัวประมวลผลล่วงหน้าจะเปลี่ยนเป็นEXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)

ขั้นตอนที่สองคือการต่อกัน:

#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)

ตอนนี้JACK_QUEEN_OTHER(USER)กลายเป็นEXPANSION2(ReSeRvEd_JACK, 1, 2, 3)

สิ่งนี้เปิดโอกาสให้เพิ่มเครื่องหมายจุลภาคจำนวนหนึ่งตามว่าสตริงตรงกันหรือไม่:

#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x

ถ้า USER เป็น JACK JACK_QUEEN_OTHER(USER)จะกลายเป็นEXPANSION2(x,x,x, 1, 2, 3)

ถ้า USER เป็น QUEEN JACK_QUEEN_OTHER(USER)จะกลายเป็นEXPANSION2(x,x, 1, 2, 3)

ถ้า USER เป็นคนอื่นJACK_QUEEN_OTHER(USER)จะกลายเป็นEXPANSION2(ReSeRvEd_other, 1, 2, 3)

ณ จุดนี้มีบางสิ่งที่สำคัญเกิดขึ้น: อาร์กิวเมนต์ที่สี่ของมาโคร EXPANSION2 เป็น 1, 2 หรือ 3 ขึ้นอยู่กับว่าอาร์กิวเมนต์เดิมที่ส่งผ่านคือแจ็คควีนหรือสิ่งอื่นใด ดังนั้นสิ่งที่เราต้องทำคือหยิบมันออกมา ด้วยเหตุผลที่ยืดยาวเราจะต้องใช้มาโครสองตัวในขั้นตอนสุดท้าย มันจะเป็น EXPANSION2 และ EXPANSION3 แม้ว่าจะดูเหมือนว่าไม่จำเป็นก็ตาม

เมื่อรวมทุกอย่างเข้าด้วยกันเรามี 6 มาโครเหล่านี้:

#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
#define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d)
#define EXPANSION3(a, b, c, d, ...) d
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x

และคุณอาจใช้สิ่งเหล่านี้:

int main() {
#if JACK_QUEEN_OTHER(USER) == 1
  printf("Hello, Jack!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 2
  printf("Hello, Queen!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 3
  printf("Hello, who are you?\n");
#endif
}

ลิงค์ Godbolt บังคับ: https://godbolt.org/z/8WGa19


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.