1. 关于一个宏出出不解的小问题

本文探讨了一个C语言程序中宏定义SQR(x)x*x的使用案例。通过具体代码示例解释了为何SQR(k+m)的计算结果为11而非预期的25,涉及宏替换的细节。

#include <stdio.h>
#define SQR(x) x*x
main()
{int a=10,k=3,m=2;
 a=SQR(k+m);  
printf("%d/n",a);
} 

为什么输出是 11呢,我是菜鸟,我不解。 

有哪位高手来看看,#define SQR(x) x*x 不是X和X的乘积嘛, 

那k+m=5,那SQR(k+m)不是等于25嘛,为什么编译器给的是11呢

如果在 **函数中直接使用 `((TYPE *)0)->MEMBER` 进行解引用**(如读取或写入成员的值),则 **一定会触发空指针错误**(如段错误 `Segmentation Fault`),因为这是在 **运行时实际访问内存地址 `0`**,而该地址通常不可访问。 但如果是 **仅计算偏移量而不解引用**(例如使用 `&` 取地址),则 **不会报错**,因为编译器仍然会优化为编译时计算,不生成实际访问内存的指令。 --- ### 关键区别:** vs. 函数中的行为** | 场景 | 代码示例 | 是否报错 | 原因 | |------|----------|----------|------| | **定义(`offsetof`)** | `#define offsetof(TYPE, MEMB) ((size_t)&((TYPE *)0)->MEMB)` | ❌ 不报错 | 编译器在编译期完成计算,不生成访问 `0` 的指令 | | **函数中仅取地址** | `size_t get_offset() { return (size_t)&((MyStruct *)0)->mem; }` | ❌ 不报错 | 编译器优化为编译期计算,无实际内存访问 | | **函数中解引用(读/写)** | `int read_mem() { return ((MyStruct *)0)->mem; }` | ✅ 报错(段错误) | 运行时尝试访问非法地址 `0` | --- ### 示例验证 #### 1. **不报错的情况(仅计算偏移量)** ```c #include <stdio.h> struct Test { int a; char b; double c; }; // 函数内计算偏移量(不报错) size_t get_b_offset() { return (size_t)&((struct Test *)0)->b; // 合法,编译器优化 } int main() { printf("Offset of b: %zu\n", get_b_offset()); // 输出正确偏移量 return 0; } ``` **结果**:正常运行,输出 `b` 的偏移量(如 `4`)。 #### 2. **报错的情况(解引用空指针)** ```c int crash() { return ((struct Test *)0)->a; // 运行时访问地址 0,触发段错误 } int main() { printf("%d\n", crash()); // 程序崩溃 return 0; } ``` **结果**:运行时崩溃(`Segmentation Fault`)。 --- ### 为什么函数内取地址也不报错? - 编译器(如 GCC、Clang)会对 `&((TYPE *)0)->MEMBER` 进行 **常量折叠**(Constant Folding),直接替换为成员偏移量,不生成访问 `0` 的指令。 - 如果关闭优化(如 `-O0`),某些编译器可能会发出警告,但仍能通过编译(因标准允许这种操作)。 --- ### 总结 - **或函数中仅计算偏移量(使用 `&`)**:安全,不报错。 - **函数中解引用(读/写)**:必然报错,因触发非法内存访问。 - **关键点**:`offsetof` 的安全性是依赖 **编译器的静态优化**,而非语言本身的运行时规则。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值