Làm thế nào, chính xác, thủ thuật chuỗi kép hoạt động như thế nào?


84

Ít nhất một số bộ tiền xử lý C cho phép bạn xâu chuỗi giá trị của một macro, thay vì tên của nó, bằng cách chuyển nó qua một macro giống hàm đến một macro khác để xâu chuỗi nó:

#define STR1(x) #x
#define STR2(x) STR1(x)
#define THE_ANSWER 42
#define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */

Các trường hợp sử dụng ví dụ ở đây .

Điều này hoạt động, ít nhất là trong GCC và Clang (cả hai với -std=c99), nhưng tôi không chắc nó hoạt động như thế nào trong các điều kiện tiêu chuẩn C.

Hành vi này có được C99 đảm bảo không?
Nếu vậy, làm thế nào để C99 đảm bảo điều đó?
Nếu không, tại thời điểm nào thì hành vi đi từ C-xác định đến GCC-xác định?


1
Nếu bạn nói "ít nhất một số", điều đó có nghĩa là bạn đã thấy một trong những nơi nó không hoạt động? Tôi sẵn sàng viết báo cáo lỗi cho nhà cung cấp.
Jens

@Jens: Không; Tôi chưa. Mọi trình biên dịch tôi đã sử dụng (cụ thể là GCC và Clang) thực hiện hành vi này.
Peter Hosey

Câu trả lời:


80

Có, nó được đảm bảo.

Nó hoạt động vì các đối số cho macro được mở rộng macro, ngoại trừ trường hợp tên đối số macro xuất hiện trong thân macro với chuỗi ký tự # hoặc mã thông báo-paster ##.

6.10.3.1/1:

... Sau khi các đối số cho lệnh gọi của một macro giống hàm đã được xác định, việc thay thế đối số diễn ra. Một tham số trong danh sách thay thế, trừ khi đứng trước mã thông báo tiền xử lý # hoặc ## hoặc theo sau là mã thông báo tiền xử lý ## (xem bên dưới), được thay thế bằng đối số tương ứng sau khi tất cả các macro chứa trong đó đã được mở rộng ...

Vì vậy, nếu bạn làm vậy STR1(THE_ANSWER)thì bạn nhận được "THE_ANSWER", bởi vì đối số của STR1 không được mở rộng macro. Tuy nhiên, đối số của STR2 được mở rộng vĩ mô khi nó được thay thế vào định nghĩa của STR2, do đó cung cấp cho STR1 một đối số 42, với kết quả là "42".


21

Như Steve lưu ý, điều này là đảm bảo và nó đã được đảm bảo kể từ tiêu chuẩn C89 - đó là tiêu chuẩn được hệ thống hóa các toán tử ### trong macro và bắt buộc mở rộng đệ quy macro trong args trước khi thay thế chúng vào nội dung nếu và chỉ khi phần nội dung không áp dụng # hoặc ## cho đối số. C99 không thay đổi so với C89 về mặt này.


+1 vì đã dành thời gian để xác nhận rằng nó cũng là một phần của C89. Một số người trong chúng ta quan tâm đến tính di động, bao gồm cả việc tạo mã mà mọi người biên dịch ở chế độ C89 có thể sử dụng - chỉ vài năm trước, các bản phát hành gcc vẫn được mặc định là C89 (cộng với các phần mở rộng gcc để công bằng), MSVC lần cuối tôi nghe nói vẫn chỉ hỗ trợ C89 và được nhúng hoặc các hệ thống kế thừa đôi khi chỉ có trình biên dịch C89. C89 tạo ra một "tầng trệt" có tính di động tuyệt vời, một tiêu chuẩn mẫu số chung ít nhất mà mọi người có thể nhắm mục tiêu để biên dịch và chạy cho bất kỳ thứ gì trong thực tế ngày nay. Vì vậy, nó là rất tốt để xem nó được ghi nhớ.
mtraceur
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.