Tuân thủ tiêu chuẩn của macro C


8

Tôi có viên ngọc nhỏ này ở đây (ý tưởng bị đánh cắp một cách vô vọng từ C-FAQ):

/* A lot of checks omitted to get rid of the architectures with a "weird" endianness */
/*...*/
#define MP_ENDIANESS ( (0x41424344ul == *(uint32_t*)"ABCD") ? MP_BIG_ENDIAN : MP_LITTLE_ENDIAN )

Có tuân thủ (đó không phải là một hành vi không xác định) với tiêu chuẩn hiện tại mới (C-18 tại thời điểm câu hỏi này đã được hỏi) và nếu có, những điều cũ hơn cũng hỗ trợ nó?

Có phải nó cũng tuân thủ tiêu chuẩn C ++? (Vâng, tôi biết về std::endian)


Nếu bạn đang nói về câu hỏi 10.1 trong danh sách C FAQ này , nó sẽ đề xuất hai kỹ thuật khác nhau, khác biệt đáng kể (AFAIK) vẫn còn hiệu lực.
Hội nghị thượng đỉnh Steve

Những thứ như thế này khiến tôi muốn thoát khỏi C ++ ...
snoopy

Câu trả lời:


10

Nó có một số vấn đề:

  • uint32_t không đảm bảo tồn tại
  • "ABCD", một mảng phân rã thành một char*(C) / char const*(C ++), không được đảm bảo để được căn chỉnh phù hợp cho uint32_t*. Nếu không, dàn diễn viên là UB
  • nếu dàn diễn viên đi qua, deref ( *(uint32_t*)"ABCD") là vi phạm bí danh nghiêm ngặt (UB)

Thay vào đó, bạn có thể muốn làm một cái gì đó như thế này:

#if !__cplusplus
    #define LITTLE_ENDIAN_EH() (*(char*)&(int){1});
#else
    //C++ doesn't have compound literals
    static int const LITTLE_ENDIAN_EH_ = 1;
    #define LITTLE_ENDIAN_EH() (*(char*)&LITTLE_ENDIAN_EH_)
#endif

(Hoạt động vì charsẽ tồn tại, có thể bí danh bất cứ điều gì và có yêu cầu căn chỉnh tối thiểu.)

Tất cả các macro, bao gồm cả các nỗ lực của bạn, đều có nhược điểm là không phù hợp với các điều kiện tiền xử lý ( #if ...) hoặc trong bối cảnh yêu cầu một biểu thức hằng số nguyên ( casenhãn, kích thước mảng, kích thước bitfield), nhưng khi được sử dụng ở nơi khác, các trình biên dịch hiện đại thường xử lý kết quả là một hằng số thời gian biên dịch liên quan đến đầu ra lắp ráp được tối ưu hóa.


Tại sao đưa ra Tiêu chuẩn C11 - 6.5 Biểu thức (p7) (viên đạn cuối cùng) bạn có thấy vi phạm bí danh nghiêm ngặt đối với C không? Các diễn viên liên quan "a character type"được phép cụ thể?
David C. Rankin

1
@ DavidC.Rankin OP đang truyền một char*/ char const*đến uint32_t*và sau đó sử dụng nó để truy cập vào đối tượng cơ bản. Nó chỉ hoạt động ngược lại, tức là khi bạn có một uint32_t*, sau đó bạn có thể truy cập nó thông qua một con trỏ ký tự.
PSkocik

1
Tôi đã có một chút giằng xé giữa bạn và @NathanOliver Bạn đã trả lời cả hai câu hỏi và từ đó đạt được điểm nhưng tuy nhiên: cảm ơn cả hai bạn!
deamentiaemundi

Nhưng không phải là diễn viên và sự uint32_ttự do cho chính nó và không phải là một con trỏ? Nói chung gcc rất giỏi trong việc gắn cờ vi phạm bí danh nghiêm ngặt và loại con trỏ bị trừng phạt, và không có khiếu nại với uint32_t u = *(uint32_t*)"ABCD";(gcc (GCC) 9.1.0)
David C. Rankin

Bất kỳ mối quan tâm cho 0x01020304các nền tảng trung lưu lưu trữ các byte 02 01 04 03hoặc 03 04 01 02?
Eljay

4

Đây không phải là hành vi được xác định trong C ++. *(uint32_t*)"ABCD"đối xử với bộ nhớ "ABCD"như thể nó là một uint32_t, nhưng vì nó không thực sự, đây là một vi phạm bí danh nghiêm ngặt và hành vi không xác định.

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.