C语言陷阱之 #if 不存在的宏
.
.
.
.
.
今天在使用 __BYTE_ORDER 宏判断字节序的时候,使用了如下的代码:
#include <stdio.h> #include <stdint.h> typedef struct relay_frame_st { #if __BYTE_ORDER == __BIG_ENDIAN uint16_t version : 4; uint16_t seq : 12; #elif __BYTE_ORDER == __LITTLE_ENDIAN uint16_t seq : 12; uint16_t version : 4; #endif uint32_t a:1; uint32_t b:1; uint32_t c; } __attribute__((packed)) Frame, *PFrame; int main (int argc, char *argv[]) { printf("sizeof=%d\n", sizeof(Frame)); Frame frame = { .seq = 0x123, .version = 0x4 }; #if __BYTE_ORDER == __BIG_ENDIAN printf("big end\n"); #elif __BYTE_ORDER == __LITTLE_ENDIAN printf("little end\n"); #else #error UNKNOWN BYTE ORDER #endif printf("frame=%X\n", *(uint16_t*)&frame); uint8_t *p = (uint8_t *) &frame; printf("%02X %02X\n", *p, *(p+1)); return 0; }
编译运行后结果是这样的:
$ gcc end.c && ./a.out sizeof=7 big end frame=4123 23 41
运行结果很奇怪,打印了 big end,但是 frame 的值却是按照小端的形式打印的:34 12。
在网上查资料,发现别人使用 __BYTE_ORDER 宏的时候包含了一个头文件,于是我也包含进去:
#include <endian.h>
再编译运行一次:
$ gcc end.c && ./a.out sizeof=7 little end frame=4123 23 41
这次正常了,看来是因为没有包含头文件导致宏未定义,所以进入了第一个条件分支。
但是恰恰问题就出现在这里:宏没有定义,为什么条件编译却进入了?非常反直觉,于是我加入了下面的代码进行测试:
#if aaaaa == bbbbb printf("aaaaa\n"); #endif
编译运行,结果你猜怎么着?竟然真的进入条件编译了。
... 省略 ...
aaaaa
... 省略 ...
于是赶紧翻书查资料,发现在《C程序设计语言》里面有这样一段话:
按照我的理解,当使用预处理指令 #if 判断某个宏的时候,如果宏的值没有定义,就会被替换为 0。于是赶紧做个实验:
结果你猜怎么着?不存在的宏还真是和 0 相等。
... 省略 ...
hhhhhhhh
... 省略 ...
这样也就解释得通为什么当两个宏都不存在的时候,条件编译却被满足了。
因为当两个宏都不存在的时候,那么在预编译器看来条件就是 (0 == 0) 呀,结果当然是 1 啦。
作者:dybai
出自:https://0xcafebabe.cnblogs.com
赞赏:3Ky9q5HVGpYseBPAUTvbJBvM3h3FQ3edqr(BTC)
本作品采用知识共享署名-相同方式共享 3.0 中国大陆许可协议进行许可。
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
posted on 2024-01-12 18:00 0xCAFEBABE 阅读(143) 评论(0) 编辑 收藏 举报