Penggabungan string makro C / C ++


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

Apakah mungkin untuk menggabungkan STR3 == "s1"? Anda dapat melakukan ini dengan meneruskan args ke fungsi Makro lainnya. Tetapi apakah ada cara langsung?


Bukankah seharusnya
— #tentukan

Seharusnya tidak demikian karena itu mendefinisikan STR3 sebagai token praproses STR1STR2. Dan meneruskan arg ke fungsi makro lain tidak membantu, karena string literal tidak dapat ditempelkan bersama - "s" "1" bukanlah token yang valid.
— Jim Balter

Jawaban:


157

Jika keduanya adalah string, Anda bisa melakukan:

#define STR3 STR1 STR2

Praprosesor secara otomatis menggabungkan string yang berdekatan.

EDIT:

Seperti disebutkan di bawah ini, bukan preprocessor tetapi kompiler yang melakukan penggabungan.


17
Secara teknis penggabungan string dilakukan di tingkat bahasa.
— Martin York

47
Praprosesor tidak melakukan hal seperti itu. Ini adalah bahasa C yang memperlakukan literal string yang berdekatan seolah-olah mereka adalah literal string tunggal.
— Jim Balter

7
Ini lebih dari sekedar teknis - Anda tidak dapat menggabungkan L"a"dan "b"mendapatkan L"ab", tetapi Anda dapat menggabungkan L"a"dan L"b"mendapatkan L"ab".
— MSalters

115

Anda tidak memerlukan solusi semacam itu untuk string literal, karena mereka digabungkan pada tingkat bahasa, dan itu tidak akan berfungsi karena "s" "1" bukan token preprocessor yang valid.

[Sunting: Menanggapi komentar "Hanya untuk dicatat" yang salah di bawah ini yang sayangnya menerima beberapa suara positif, saya akan mengulangi pernyataan di atas dan mengamati bahwa fragmen program

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

menghasilkan pesan kesalahan ini dari fase pra-pemrosesan gcc: kesalahan: menempelkan "" s "" dan "" 1 "" tidak memberikan token pra-pemrosesan yang valid

]

Namun, untuk penempelan token umum, coba ini:

/*
 * 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)

Kemudian, misalnya, keduanya PPCAT_NX(s, 1)dan PPCAT(s, 1)menghasilkan pengenal s1, kecuali jika sdidefinisikan sebagai makro, dalam hal ini PPCAT(s, 1)menghasilkan <macro value of s>1.

Melanjutkan tema adalah makro ini:

/*
 * 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)

Kemudian,

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

Sebaliknya,

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
Sekadar catatan, "s""1"valid dalam C (dan C ++). Mereka adalah dua token (string literal) yang akan disatukan oleh kompiler dan ancaman sebagai satu token.
— Shahbaz

4
Anda salah paham baik komentar saya maupun bahasa C. Saya berkata "s""1" isn't a valid token- itu benar; itu, seperti yang Anda katakan, dua token. Tetapi menyatukannya dengan ## akan membuatnya menjadi token praproses tunggal , bukan dua token, sehingga compiler tidak akan melakukan penggabungan, melainkan lexer akan menolaknya (bahasa ini memerlukan diagnostik).
— Jim Balter

8
@ mr5 Bacalah komentar dengan cermat. Nama makro yang diteruskan sebagai argumen makro tidak diperluas sebelum diteruskan. Namun, mereka diperluas dalam tubuh makro. Jadi jika A didefinisikan sebagai FRED, STRINGIZE_NX (A) diperluas menjadi "A" tetapi STRINGIZE (A) meluas menjadi STRINGIZE_NX (FRED) yang meluas menjadi "FRED".
— Jim Balter

1
@bharath string yang dihasilkan adalah "PPCAT (T1, T2)" - seperti yang diharapkan dan diinginkan. dan bukan "s1" yang diharapkan - tidak diharapkan sama sekali. Mengapa kita membutuhkan tipu muslihat / bersarang ekstra? - Baca komentar kode, dan komentar saya di atas dengan 6 suara positif. Hanya badan makro yang diperluas; di luar badan makro, argumen makro di antara tanda kurung tidak diperluas sebelum diteruskan ke makro. Jadi STRINGIZE_NX(whatever occurs here)berkembang menjadi "apa pun yang terjadi di sini", terlepas dari definisi makro apa pun untuk apa pun, yang terjadi, atau di sini.
— Jim Balter

1
@bharath Tentu saja tidak mencetak "Nama A" - A adalah nama parameter, bukan argumen ke makro, yaitu ALEX. Anda mengklaim if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- itu salah, dan tidak seperti ujian Anda. Anda berusaha keras untuk tidak memahami atau melakukan ini dengan benar, dan saya tidak akan menanggapi Anda lebih jauh.
— Jim Balter

24

Petunjuk: STRINGIZEMakro di atas keren, tetapi jika Anda membuat kesalahan dan argumennya bukan makro - Anda memiliki kesalahan ketik pada namanya, atau lupa #includepada file header - maka kompilator akan dengan senang hati memasukkan nama makro yang diklaim ke dalam string tanpa kesalahan.

Jika Anda bermaksud agar argumen ke STRINGIZEselalu makro dengan nilai C normal, maka

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

akan mengembangkannya sekali dan memeriksa validitasnya, membuangnya, lalu mengembangkannya lagi menjadi string.

Butuh beberapa saat bagi saya untuk mencari tahu mengapa STRINGIZE(ENOENT)berakhir sebagai "ENOENT"alih-alih "2"... Saya tidak memasukkan errno.h.


2
Pengamatan penting, dan +1 untuk penggunaan ,operator yang tepat. :)
— Jesse Chisholm

2
Tidak ada alasan khusus mengapa konten string harus berupa ekspresi C yang valid. Jika Anda ingin melakukannya, saya sarankan untuk memberinya nama yang berbeda, seperti STRINGIZE_EXPR.
— Jim Balter

Trik itu mungkin berhasil dalam isolasi. Tapi itu mencegah kompilator melihat urutan string yang akan digabungkannya. (menghasilkan urutan seperti, ((1),"1") "." ((2),"2")bukan hanya "1" "." "2")
— automorfik

Hanya untuk memperjelas apa yang dikatakan automorfik: dengan STRINGIZEdefinisi aslinya , "The value of ENOENT is " STRINGIZE(ENOENT)berfungsi, sedangkan "The value of ENOENT is" STRINGIZE_EXPR(X)menghasilkan kesalahan.
— Jim Balter
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.