Łą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.