Nối chuỗi macro C / C ++


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

Có thể nối có STR3 == "s1" không? Bạn có thể làm điều này bằng cách chuyển args cho một hàm Macro khác. Nhưng có cách nào trực tiếp không?


Không phải là #define STR3 STR1 ## STR2
Shrinidhi

Cũng không nên vì điều đó xác định STR3 là mã thông báo tiền xử lý STR1STR2. Và việc chuyển args sang một hàm macro khác cũng không giúp ích được gì, vì không thể dán các ký tự chuỗi với nhau - "s" "1" không phải là mã thông báo hợp lệ.
Jim Balter

Câu trả lời:


157

Nếu chúng là cả hai chuỗi, bạn chỉ có thể làm:

#define STR3 STR1 STR2

Bộ tiền xử lý tự động nối các chuỗi liền kề.

BIÊN TẬP:

Như đã lưu ý bên dưới, nó không phải là bộ tiền xử lý mà là bộ biên dịch thực hiện quá trình nối.


17
Kỹ thuật nối chuỗi được thực hiện ở cấp độ ngôn ngữ.
Martin York

47
Bộ tiền xử lý không làm điều đó. Đó là ngôn ngữ C thích hợp xử lý các ký tự chuỗi liền kề như thể chúng là một ký tự chuỗi đơn.
Jim Balter

7
Nó còn hơn cả tính kỹ thuật - bạn không thể nối L"a""b"lấy L"ab", nhưng bạn có thể nối L"a"L"b"lấy L"ab".
MSalters

115

Bạn không cần loại giải pháp đó cho các ký tự chuỗi, vì chúng được nối ở cấp độ ngôn ngữ và nó sẽ không hoạt động bởi vì "s" "1" không phải là mã thông báo tiền xử lý hợp lệ.

[Chỉnh sửa: Để trả lời cho nhận xét không chính xác "Chỉ cho bản ghi" bên dưới mà không may nhận được một số phiếu tán thành, tôi sẽ nhắc lại tuyên bố ở trên và quan sát rằng phân đoạn chương trình

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

tạo ra thông báo lỗi này từ giai đoạn tiền xử lý của gcc: error: việc dán "" s "" và "" 1 "" không cung cấp mã thông báo tiền xử lý hợp lệ

]

Tuy nhiên, để dán mã thông báo chung, hãy thử cách này:

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

Sau đó, ví dụ, cả hai PPCAT_NX(s, 1)PPCAT(s, 1)tạo ra số nhận dạng s1, trừ khis được định nghĩa là một macro, trong trường hợp đó sẽ PPCAT(s, 1)tạo ra <macro value of s>1.

Tiếp tục chủ đề là các macro sau:

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

Sau đó,

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

Ngược lại,

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
Chỉ cho bản ghi, "s""1"hợp lệ trong C (và C ++). Chúng là hai mã thông báo (chuỗi ký tự) mà trình biên dịch sẽ tự ghép nối và đe dọa như một mã thông báo.
Shahbaz,

4
Bạn hiểu sai cả nhận xét của tôi và ngôn ngữ C. Tôi nói "s""1" isn't a valid token- điều đó chính xác; như bạn nói, nó là hai mã thông báo. Nhưng việc gộp chúng lại với nhau bằng ## sẽ khiến chúng trở thành một mã thông báo tiền xử lý duy nhất , không phải hai mã thông báo và do đó trình biên dịch sẽ không thực hiện ghép nối, thay vì lexer sẽ từ chối chúng (ngôn ngữ yêu cầu chẩn đoán).
Jim Balter,

8
@ mr5 Đọc các nhận xét, cẩn thận. Tên macro được truyền dưới dạng đối số macro không được mở rộng trước khi được truyền. Tuy nhiên, chúng được mở rộng trong phần nội dung của macro. Vì vậy, nếu A được xác định là FRED, thì STRINGIZE_NX (A) mở rộng thành "A" nhưng STRINGIZE (A) mở rộng thành STRINGIZE_NX (FRED) mở rộng thành "FRED".
Jim Balter,

1
@bharath chuỗi kết quả là "PPCAT (T1, T2)" - như mong đợi và mong muốn. và không phải là "s1" được mong đợi - hoàn toàn không được mong đợi. Tại sao chúng ta cần thêm một hướng dẫn / lồng ghép? - Đọc các bình luận về mã, và bình luận của tôi ở trên với 6 lượt tán thành. Chỉ phần thân của macro được mở rộng; bên ngoài phần thân macro, các đối số macro giữa các dấu ngoặc đơn không được mở rộng trước khi được chuyển đến macro. Vì vậy, STRINGIZE_NX(whatever occurs here)mở rộng thành "bất cứ điều gì xảy ra ở đây", bất kể bất kỳ định nghĩa vĩ mô nào cho bất kỳ điều gì xảy ra, xảy ra hoặc ở đây.
Jim Balter

1
@bharath Tất nhiên nó không in "Tên A" - A là tên tham số, không phải là đối số của macro, là ALEX. Bạn đã tuyên bố if A is defined as FRED then STRINGIZE_NX(A) still expands to "FRED"- điều đó là sai và không giống như bài kiểm tra của bạn. Bạn đang cố gắng không hiểu hoặc hiểu đúng điều này và tôi sẽ không trả lời bạn thêm.
Jim Balter

24

Gợi ý: STRINGIZEMacro ở trên rất tuyệt, nhưng nếu bạn mắc lỗi và đối số của nó không phải là macro - bạn có lỗi đánh máy trong tên hoặc quên #includetệp tiêu đề - thì trình biên dịch sẽ vui vẻ đặt tên macro có mục đích vào chuỗi không có lỗi.

Nếu bạn dự định rằng đối số STRINGIZEluôn là macro có giá trị C bình thường, thì

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

sẽ mở rộng nó một lần và kiểm tra xem nó có hợp lệ không, loại bỏ nó và sau đó mở rộng lại thành một chuỗi.

Tôi đã mất một lúc để tìm ra lý do tại sao STRINGIZE(ENOENT) lại kết thúc "ENOENT"thay vì "2"... tôi đã không bao gồm errno.h.


2
Quan sát quan trọng và +1 để sử dụng đúng ,nhà điều hành. :)
Jesse Chisholm

2
Không có lý do cụ thể nào tại sao nội dung của chuỗi phải là một biểu thức C hợp lệ. Nếu bạn muốn làm điều này, tôi khuyên bạn nên đặt tên khác, chẳng hạn như STRINGIZE_EXPR.
Jim Balter

Thủ thuật đó có thể đã hoạt động một cách cô lập. Nhưng nó ngăn trình biên dịch nhìn thấy một chuỗi các chuỗi mà nó sẽ nối. (dẫn đến các chuỗi như ((1),"1") "." ((2),"2")thay vì chỉ "1" "." "2")
tự động hóa

Chỉ để làm rõ tự động đang nói gì: với STRINGIZEđịnh nghĩa ban đầu , "The value of ENOENT is " STRINGIZE(ENOENT)hoạt động, trong khi "The value of ENOENT is" STRINGIZE_EXPR(X)tạo ra lỗi.
Jim Balter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.