การต่อสตริงมาโคร C / C ++


121
#define STR1      "s"
#define STR2      "1"
#define STR3      STR1 ## STR2

เป็นไปได้หรือไม่ที่จะเชื่อมต่อกันโดยมี STR3 == "s1" คุณสามารถทำได้โดยส่ง args ไปยังฟังก์ชัน Macro อื่น แต่มีทางตรงไหม?


ไม่ควรเป็น #define STR3 STR1 ## STR2
Shrinidhi

ไม่ควรเป็นอย่างใดอย่างหนึ่งเนื่องจากกำหนดให้ STR3 เป็นโทเค็นก่อนการประมวลผล STR1STR2 และการส่ง args ไปยังฟังก์ชันแมโครอื่นก็ไม่ได้ช่วยอะไรเพราะไม่สามารถวางสตริงลิเทอรัลร่วมกันได้ - "s" "1" ไม่ใช่โทเค็นที่ถูกต้อง
Jim Balter

คำตอบ:


157

หากเป็นทั้งสองสตริงคุณสามารถทำได้:

#define STR3 STR1 STR2

ตัวประมวลผลก่อนเชื่อมต่อสตริงที่อยู่ติดกันโดยอัตโนมัติ

แก้ไข:

ดังที่ระบุไว้ด้านล่างไม่ใช่ตัวประมวลผลก่อน แต่เป็นคอมไพเลอร์ที่ทำการเชื่อมต่อ


17
การต่อสายอักขระในทางเทคนิคจะกระทำที่ระดับภาษา
Martin York

47
พรีโปรเซสเซอร์ไม่ทำสิ่งนั้น เป็นภาษา C ที่เหมาะสมซึ่งถือว่าตัวอักษรสตริงที่อยู่ติดกันราวกับว่าเป็นลิเทอรัลสตริงเดียว
Jim Balter

7
มันมากกว่าวิชา - คุณไม่สามารถ concatenate L"a"และ"b"จะได้รับL"ab"แต่คุณสามารถเชื่อมL"a"และที่จะได้รับL"b" L"ab"
MSalters

115

คุณไม่จำเป็นต้องใช้โซลูชันประเภทนั้นสำหรับสตริงลิเทอรัลเนื่องจากมีการเชื่อมต่อกันที่ระดับภาษาและจะใช้ไม่ได้อยู่ดีเนื่องจาก "s" "1" ไม่ใช่โทเค็นตัวประมวลผลล่วงหน้าที่ถูกต้อง

[แก้ไข: เพื่อตอบสนองต่อความคิดเห็น "Just for the record" ที่ไม่ถูกต้องด้านล่างซึ่งน่าเสียดายที่ได้รับการโหวตหลายครั้งฉันจะย้ำข้อความข้างต้นและสังเกตว่าส่วนของโปรแกรม

#define PPCAT_NX(A, B) A ## B
PPCAT_NX("s", "1")

สร้างข้อความแสดงข้อผิดพลาดนี้จากขั้นตอนก่อนการประมวลผลของ gcc: ข้อผิดพลาด: การวาง "" s "" และ "" 1 "" ไม่ได้ให้โทเค็นการประมวลผลล่วงหน้าที่ถูกต้อง

]

อย่างไรก็ตามสำหรับการวางโทเค็นทั่วไปให้ลองทำดังนี้:

/*
 * Concatenate preprocessor tokens A and B without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define PPCAT_NX(A, B) A ## B

/*
 * Concatenate preprocessor tokens A and B after macro-expanding them.
 */
#define PPCAT(A, B) PPCAT_NX(A, B)

จากนั้นเช่นทั้งสองPPCAT_NX(s, 1)และPPCAT(s, 1)ผลิตระบุs1เว้นแต่sถูกกำหนดให้เป็นมาโครซึ่งในกรณีที่ผลิตPPCAT(s, 1)<macro value of s>1

การดำเนินการต่อในธีมคือมาโครเหล่านี้:

/*
 * Turn A into a string literal without expanding macro definitions
 * (however, if invoked from a macro, macro arguments are expanded).
 */
#define STRINGIZE_NX(A) #A

/*
 * Turn A into a string literal after macro-expanding it.
 */
#define STRINGIZE(A) STRINGIZE_NX(A)

จากนั้น

#define T1 s
#define T2 1
STRINGIZE(PPCAT(T1, T2)) // produces "s1"

ตรงกันข้าม,

STRINGIZE(PPCAT_NX(T1, T2)) // produces "T1T2"
STRINGIZE_NX(PPCAT_NX(T1, T2)) // produces "PPCAT_NX(T1, T2)"

#define T1T2 visit the zoo
STRINGIZE(PPCAT_NX(T1, T2)) // produces "visit the zoo"
STRINGIZE_NX(PPCAT(T1, T2)) // produces "PPCAT(T1, T2)"

8
สำหรับบันทึกเท่านั้น"s""1"ใช้ได้ใน C (และ C ++) เป็นโทเค็นสองโทเค็น (ตัวอักษรสตริง) ที่คอมไพเลอร์จะเชื่อมต่อตัวเองและคุกคามเป็นโทเค็นเดียว
Shahbaz

4
คุณเข้าใจผิดทั้งความคิดเห็นของฉันและภาษาซี ฉันพูด"s""1" isn't a valid token- ถูกต้อง; มันเป็นอย่างที่คุณพูดสองโทเค็น แต่การยึดเข้าด้วยกันด้วย ## จะทำให้โทเค็นเป็นโทเค็นก่อนการประมวลผลเดียวไม่ใช่สองโทเค็นดังนั้นคอมไพเลอร์จะไม่ทำการเชื่อมต่อกัน แต่ตัวอักษรจะปฏิเสธ (ภาษาต้องมีการวินิจฉัย)
Jim Balter

8
@ mr5 อ่านความคิดเห็นอย่างระมัดระวัง ชื่อแมโครที่ส่งผ่านเป็นอาร์กิวเมนต์แมโครจะไม่ขยายก่อนที่จะส่งผ่าน อย่างไรก็ตามมีการขยายในเนื้อหาของมาโคร ดังนั้นหากกำหนด A เป็น FRED STRINGIZE_NX (A) จะขยายเป็น "A" แต่ STRINGIZE (A) จะขยายเป็น STRINGIZE_NX (FRED) ซึ่งขยายเป็น "FRED"
Jim Balter

1
@bharath สตริงผลลัพธ์คือ "PPCAT (T1, T2)" - ตามที่คาดไว้และต้องการ และไม่ใช่ "s1" ที่คาดหวัง - ไม่คาดคิดเลย ทำไมเราต้องมีทิศทาง / การซ้อนกันเป็นพิเศษ? - อ่านความคิดเห็นเกี่ยวกับรหัสและความคิดเห็นของฉันด้านบนด้วยการโหวต 6 รายการ ขยายเฉพาะส่วนของมาโคร ภายนอกมาโครอาร์กิวเมนต์มาโครระหว่างวงเล็บจะไม่ขยายก่อนที่จะส่งต่อไปยังมาโคร ดังนั้นSTRINGIZE_NX(whatever occurs here)ขยายเป็น "อะไรก็ตามที่เกิดขึ้นที่นี่" โดยไม่คำนึงถึงคำจำกัดความมาโครสำหรับสิ่งใดก็ตามเกิดขึ้นหรือที่นี่
Jim Balter

1
@bharath แน่นอนว่ามันไม่ได้พิมพ์ "Name A" - A คือชื่อพารามิเตอร์ไม่ใช่อาร์กิวเมนต์ของมาโครซึ่งก็คือ ALEX คุณอ้างว่าif A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- นั่นเป็นเท็จและไม่เหมือนกับการทดสอบของคุณ คุณกำลังพยายามอย่างยิ่งที่จะไม่เข้าใจหรือทำให้ถูกต้องและฉันจะไม่ตอบกลับคุณต่อไป
Jim Balter

24

คำแนะนำ: STRINGIZEมาโครด้านบนนั้นยอดเยี่ยม แต่ถ้าคุณทำผิดพลาดและอาร์กิวเมนต์ไม่ใช่มาโคร - คุณพิมพ์ชื่อผิดหรือลืม#includeไฟล์ส่วนหัวจากนั้นคอมไพเลอร์จะใส่ชื่อมาโครที่อ้างว่ามีความสุขลงใน สตริงที่ไม่มีข้อผิดพลาด

หากคุณตั้งใจให้อาร์กิวเมนต์STRINGIZEเป็นมาโครที่มีค่า C ปกติเสมอ

#define STRINGIZE(A) ((A),STRINGIZE_NX(A))

จะขยายอีกครั้งและตรวจสอบความถูกต้องทิ้งสิ่งนั้นแล้วขยายอีกครั้งเป็นสตริง

มันเอาฉันในขณะที่จะคิดออกว่าทำไมSTRINGIZE(ENOENT)กำลังจะจบสิ้นขึ้นเป็น"ENOENT"แทน"2"... errno.hผมไม่ได้รวม


2
ข้อสังเกตที่สำคัญและ +1 เพื่อการใช้งานตัว,ดำเนินการอย่างเหมาะสม :)
Jesse Chisholm

2
ไม่มีเหตุผลเฉพาะว่าทำไมเนื้อหาของสตริงจึงควรเป็นนิพจน์ C ที่ถูกต้อง หากคุณต้องการดำเนินการนี้ขอแนะนำให้ตั้งชื่ออื่นเช่น STRINGIZE_EXPR
Jim Balter

เคล็ดลับนั้นอาจใช้การได้อย่างโดดเดี่ยว แต่จะป้องกันไม่ให้คอมไพเลอร์เห็นลำดับของสตริงที่จะเชื่อมต่อกัน (ส่งผลให้ลำดับเหมือน((1),"1") "." ((2),"2")แทนที่จะเป็น "1" "." "2")
automorphic

เพียงเพื่อชี้แจงว่า Automorphic พูดถึงอะไร: ด้วยSTRINGIZEคำจำกัดความดั้งเดิมใช้"The value of ENOENT is " STRINGIZE(ENOENT)งานได้ในขณะที่"The value of ENOENT is" STRINGIZE_EXPR(X)เกิดข้อผิดพลาด
Jim Balter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.