Łączenie ciągów makr C / C ++


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

Czy można łączyć, że STR3 == "s1"? Możesz to zrobić, przekazując argumenty do innej funkcji Macro. Ale czy istnieje bezpośredni sposób?


Czy nie powinno to być #define STR3 STR1 ##
STR2

Nie powinno też być, ponieważ to definiuje STR3 jako token przetwarzania wstępnego STR1STR2. A przekazywanie argumentów do innej funkcji makra nie pomaga, ponieważ literały łańcuchowe nie mogą być wklejane razem - „s” „1” nie jest prawidłowym tokenem.
Jim Balter,

Odpowiedzi:


157

Jeśli to oba ciągi, możesz po prostu zrobić:

#define STR3 STR1 STR2

Preprocesor automatycznie łączy sąsiednie ciągi.

EDYTOWAĆ:

Jak zauważono poniżej, to nie preprocesor, ale kompilator wykonuje konkatenację.


17
Technicznie konkatenacja ciągów znaków jest wykonywana na poziomie języka.
Martin York,

47
Preprocesor nic takiego nie robi. Jest to właściwy język C, który traktuje sąsiednie literały łańcuchowe tak, jakby były one literałem pojedynczego ciągu.
Jim Balter,

7
To więcej niż technicyzacji - nie można concatenate L"a"i "b"dostać L"ab", ale można złączyć L"a"i L"b"dostać L"ab".
MSalters

115

Nie potrzebujesz tego rodzaju rozwiązania dla literałów łańcuchowych, ponieważ są one konkatenowane na poziomie języka, a i tak by nie zadziałało, ponieważ „s” „1” nie jest prawidłowym tokenem preprocesora.

[Edytuj: W odpowiedzi na niepoprawny komentarz "Tylko do zapisu" poniżej, który niestety otrzymał kilka głosów za, powtórzę powyższe stwierdzenie i zauważę, że fragment programu

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

generuje ten komunikat o błędzie z fazy przetwarzania wstępnego gcc: błąd: wklejanie "" s "" i "1" "nie daje prawidłowego tokena przetwarzania wstępnego

]

Jednak w przypadku ogólnego wklejania tokenów spróbuj tego:

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

Następnie, na przykład, oba PPCAT_NX(s, 1)i PPCAT(s, 1)produkują identyfikator s1, chyba że sjest zdefiniowane jako makro, w którym to przypadku PPCAT(s, 1)produkuje <macro value of s>1.

Kontynuacją tematu są te makra:

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

Następnie,

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

Natomiast

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
Dla przypomnienia, "s""1"obowiązuje w C (i C ++). Są to dwa tokeny (literały ciągów), które kompilator połączy ze sobą i zagrozi jako jeden token.
Shahbaz

4
Źle rozumiesz zarówno mój komentarz, jak i język C. Powiedziałem "s""1" isn't a valid token- to jest poprawne; są to, jak mówisz, dwa żetony. Ale połączenie ich razem z ## uczyniłoby je pojedynczym tokenem przetwarzania wstępnego, a nie dwoma, więc kompilator nie wykonałby konkatenacji, a lekser odrzuciłby je (język wymaga diagnostyki).
Jim Balter,

8
@ mr5 Przeczytaj uważnie komentarze. Nazwy makr przekazane jako argumenty makr nie są rozwijane przed przekazaniem. Są one jednak rozszerzone w treści makra. Więc jeśli A jest zdefiniowane jako FRED, STRINGIZE_NX (A) rozwija się do „A”, ale STRINGIZE (A) rozwija się do STRINGIZE_NX (FRED), który rozwija się do „FRED”.
Jim Balter,

1
@bharath wynikowy ciąg to „PPCAT (T1, T2)” - zgodnie z oczekiwaniami i oczekiwaniami. a nie oczekiwane „s1” - w ogóle nie oczekiwane. Dlaczego potrzebujemy dodatkowego pośredniego / zagnieżdżenia? - Przeczytaj komentarze do kodu i mój komentarz powyżej z 6 głosami za. Rozwijane są tylko ciała makr; poza ciałami makr, argumenty makr w nawiasach nie są rozwijane przed przekazaniem do makr. STRINGIZE_NX(whatever occurs here)Rozwija się więc do „cokolwiek tu się dzieje”, niezależnie od jakichkolwiek makdefinicji tego, co się dzieje, lub tutaj.
Jim Balter,

1
@bharath Oczywiście nie wypisuje „Name A” - A to nazwa parametru, a nie argument makra, którym jest ALEX. Twierdziłeś if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- to nieprawda i w niczym nie przypomina twojego testu. Bardzo starasz się nie zrozumieć lub nie zrobić tego dobrze, i nie zamierzam ci dalej odpowiadać.
Jim Balter,

24

Wskazówka: STRINGIZEpowyższe makro jest fajne, ale jeśli popełnisz błąd i jego argument nie jest makrem - miałeś literówkę w nazwie lub zapomniałeś #includepliku nagłówkowego - kompilator szczęśliwie umieści rzekomą nazwę makra w ciąg bez błędu.

Jeśli zamierzasz, aby argumentem STRINGIZEzawsze było makro z normalną wartością C, to

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

rozwinie go raz i sprawdzi, czy jest poprawny, odrzuci to, a następnie ponownie przekształci w łańcuch.

Zajęło mi trochę czasu, zanim zrozumiałem , dlaczego STRINGIZE(ENOENT)skończyło się na tym, że "ENOENT"zamiast "2"… nie uwzględniłem errno.h.


2
Ważna uwaga i +1 za prawidłowe użycie ,operatora. :)
Jesse Chisholm

2
Nie ma konkretnego powodu, dla którego zawartość ciągu powinna być prawidłowym wyrażeniem w C. Jeśli chcesz to zrobić, radzę nadać mu inną nazwę, na przykład STRINGIZE_EXPR.
Jim Balter

Ta sztuczka mogła zadziałać w izolacji. Ale uniemożliwia kompilatorowi zobaczenie sekwencji ciągów, które będzie łączył. (skutkuje sekwencjami takimi jak ((1),"1") "." ((2),"2")zamiast „1” ”.„ „2”)
automorficzny

Wystarczy wyjaśnić, co mówi automorfika: z oryginalną STRINGIZEdefinicją "The value of ENOENT is " STRINGIZE(ENOENT)działa, podczas gdy "The value of ENOENT is" STRINGIZE_EXPR(X)powoduje błąd.
Jim Balter
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.