C / C ++ makro dizesi birleştirme


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

STR3 == "s1" ile birleştirmek mümkün mü? Bunu, başka bir Makro işlevine argümanlar aktararak yapabilirsiniz. Ama doğrudan bir yol var mı?



İkisi de olmamalıdır çünkü bu, STR3'ü önişleme simgesi STR1STR2 olarak tanımlar. Ve bağımsız değişkenleri başka bir makro işlevine aktarmak yardımcı olmaz, çünkü dizge değişmezleri birbirine yapıştırılamaz - "s" "1" geçerli bir simge değildir.
Jim Balter

Yanıtlar:


157

Her ikisi de dizelerdeyse, şunları yapabilirsiniz:

#define STR3 STR1 STR2

Önişlemci, bitişik dizeleri otomatik olarak birleştirir.

DÜZENLE:

Aşağıda belirtildiği gibi, birleştirmeyi yapan ön işlemci değil derleyicidir.


17
Teknik olarak dizgi birleştirme dil seviyesinde yapılır.
Martin York

47
Önişlemci böyle bir şey yapmaz. Bitişik dize değişmezlerini tek bir dizgeymiş gibi ele alan uygun C dilidir.
Jim Balter

7
Daha teknik bir daha - sen concatenate olamaz L"a"ve "b"almak için L"ab", ama sen yapabilirsiniz bitiştirmek L"a"ve L"b"almak için L"ab".
MSalters

115

Dize değişmezleri için bu tür bir çözüme ihtiyacınız yoktur, çünkü bunlar dil düzeyinde birleştirilir ve yine de çalışmaz çünkü "s" "1" geçerli bir önişlemci simgesi değildir.

[Düzenleme: Maalesef birkaç olumlu oy almış olan aşağıdaki yanlış "Sadece kayıt için" yorumuna yanıt olarak, yukarıdaki ifadeyi tekrarlayacağım ve program parçasının

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

gcc'nin ön işleme aşamasından bu hata mesajını üretir: hata: "" s "" ve "" 1 "" öğelerinin yapıştırılması geçerli bir ön işleme jetonu vermez

]

Ancak, genel simge yapıştırmak için şunu deneyin:

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

Daha sonra, örneğin, her ikisi de PPCAT_NX(s, 1)ve PPCAT(s, 1)tanımlayıcı üretmek s1sürece, sbir kasa, bir makro olarak tanımlanmaktadır, PPCAT(s, 1)oluşturur <macro value of s>1.

Temada devam eden şu makrolar:

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

Sonra,

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

Aksine,

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
Sadece kayıt için, "s""1"C (ve C ++) 'da geçerlidir. Derleyicinin kendisini bir araya getirip tek bir belirteç olarak tehdit edeceği iki simgedir (string değişmezleri).
Shahbaz

4
Hem yorumumu hem de C dilini yanlış anlıyorsunuz. Dedim "s""1" isn't a valid token- bu doğru; dediğiniz gibi, iki jeton. Ancak bunları ## ile birlikte birleştirmek, onları iki jeton değil, tek bir ön işleme jetonu haline getirir ve böylece derleyici birleştirme yapmaz, bunun yerine sözcü bunları reddeder (dil bir teşhis gerektirir).
Jim Balter

8
@ mr5 Yorumları dikkatlice okuyun. Makro bağımsız değişkenleri olarak iletilen makro adları, aktarılmadan önce genişletilmez. Bununla birlikte, makro gövdesi içinde genişler. Dolayısıyla, A, FRED olarak tanımlanırsa, STRINGIZE_NX (A) "A" olarak genişler ancak STRINGIZE (A) "FRED" olarak genişleyen STRINGIZE_NX (FRED) olarak genişler.
Jim Balter

1
@bharath sonuçta elde edilen dize "PPCAT (T1, T2)" - beklendiği ve istendiği gibi. ve beklenen "s1" değil - hiç beklenmiyor. Neden fazladan bir yönlendirme / yerleştirmeye ihtiyacımız var? - Kod yorumlarını ve yukarıdaki 6 olumlu oyla benim yorumumu okuyun. Yalnızca makro gövdeleri genişletilir; Makro organlarının dışında, parantez arasında makro argümanlar vardır değil makrolar geçirilmeden önce genişletti. Böylece STRINGIZE_NX(whatever occurs here), her ne olursa olsun, meydana gelen veya burada herhangi bir makro tanımlamadan bağımsız olarak "burada ne olursa olsun" şeklinde genişler.
Jim Balter

1
@bharath Elbette "Ad A" yazmıyor - A, ALEX olan makronun argümanı değil, parametre adıdır. İddia ettiniz if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- bu yanlış ve testiniz gibi değil. Bunu anlamamak veya doğru anlamamak için çok uğraşıyorsun ve ben sana daha fazla cevap vermeyeceğim.
Jim Balter

24

İpucu: Yukarıdaki STRINGIZEmakro harikadır, ancak bir hata yaparsanız ve argümanı bir makro değilse - adda bir yazım hatası yapmışsanız veya #includebaşlık dosyasını unuttuysanız - derleyici, sözde makro adını mutlu bir şekilde hatasız dize.

Argümanının STRINGIZEher zaman normal C değerine sahip bir makro olmasını amaçlıyorsanız, o zaman

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

bir kez genişletir ve geçerliliğini kontrol eder, atar ve sonra tekrar bir dizeye genişletir.

Dahil etmemiştim yerine neden STRINGIZE(ENOENT)bittiğini anlamam biraz zaman aldı ."ENOENT""2"errno.h


2
,Operatörün doğru kullanımı için önemli gözlem ve +1 . :)
Jesse Chisholm

2
Dizenin içeriğinin geçerli bir C ifadesi olması için özel bir neden yoktur. Bunu yapmak istiyorsanız, STRINGIZE_EXPR gibi farklı bir ad vermenizi tavsiye ederim.
Jim Balter

Bu numara tek başına işe yaramış olabilir. Ancak derleyicinin birleştireceği bir dizi dizeyi görmesini engeller. ( ((1),"1") "." ((2),"2")sadece "1" "yerine" "2" gibi dizilerle sonuçlanır )
otomorfik

Sadece otomorfiğin ne dediğini açıklığa kavuşturmak için: orijinal STRINGIZEtanımla "The value of ENOENT is " STRINGIZE(ENOENT)çalışır, oysa "The value of ENOENT is" STRINGIZE_EXPR(X)bir hata üretir.
Jim Balter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.