C语言空指针减法在现代平台与常用编译器下的使用安全性咨询
结论先行
这类空指针减法代码绝对不建议在生产环境使用,哪怕在常见平台上临时测试能跑通,也存在明确的风险。
1. 标准层面的定性
C语言标准明确规定:只有指向同一个数组内元素的两个指针才能执行减法操作,空指针与任意对象指针的减法属于未定义行为,这也是Clang会对该操作抛出警告的根本原因。
未定义行为意味着C标准没有对这种操作的结果做任何约束,编译器完全可以按照自己的逻辑处理。
2. 为什么过去能正常运行?
在我们常用的32/64位x86、ARM、RISC-V等平台,操作系统都采用flat线性内存模型,空指针的底层数值就是0,(char *)a - (char *)0的操作本质上等价于取指针a对应的数值地址,只要编译器没有做激进的优化,这段代码的运行结果就符合预期,这也是BSD系实现用了很多年没出问题的原因。
3. 现代编译器的风险
现在GCC、Clang等主流编译器的高优化等级都会利用「未定义行为不会出现在合法代码中」的假设做优化,比如会直接判定空指针减法的结果无意义,直接删除相关的分支逻辑,或者返回错误的结果。FreeBSD移除相关代码的原因就是如此:GCC高版本优化时直接把涉及空指针减法的对齐检查逻辑优化失效,导致qsort运行出错,最终只能把这段代码删掉。
4. 替代方案
完全不需要用这种有风险的写法,要实现相同的对齐判断逻辑,用标准规定的合法写法就可以:将指针转换为uintptr_t类型后再做取模运算,效果完全一致,不存在任何兼容和优化风险:
#define TYPE_ALIGNED(TYPE, a, es) \ (((uintptr_t)a) % sizeof(TYPE) == 0 && es % sizeof(TYPE) == 0)
内容的提问来源于stack exchange,提问作者Szabolcs
相关产品推荐
相关产品推荐

