MSVC va_arg宏中(sizeof(t)&(sizeof(t)-1))!=0判断逻辑解析
问题解答
首先明确结论:这个子表达式绝非恒假,存在大量可以让条件成立的类型t。
位运算判断的逻辑
(sizeof(t) & (sizeof(t) - 1)) != 0是位运算领域的经典判断写法,作用就是检测一个正整数是不是2的整数次幂:
- 若正整数n是2的整数次幂,二进制表示一定只有1位为1,形如
100...0;n-1的二进制对应为011...1,两者做按位与运算结果必然为0 - 若正整数n不是2的整数次幂,二进制表示至少有2位为1,n减1只会翻转最低位的1及其后的低位,更高位的1会完整保留,按位与的结果一定不为0
也就是说,这个子表达式成立的条件非常简单:只要类型t的大小不是2的整数次幂即可。C语言标准从未规定所有类型的大小必须是2的幂,这类类型在实际开发中非常常见。
MSVC环境下的实际触发例子
以下类型都可以让这个判断条件成立:
- 32位x86架构MSVC下的
long double类型:对应x87 FPU的80位扩展精度浮点数,sizeof(long double)固定为12。代入计算:12 & 11 = 8 != 0,完全满足条件。 - 自定义结构体类型:只要成员组合+对齐规则计算出的总大小不是2的幂即可,比如32位MSVC默认对齐规则下:
这个结构体两个int占8字节,末尾char占1字节,最后补3字节对齐到4字节边界,总大小为12,同样满足判断条件。struct Demo { int val1; int val2; char flag; }; - 所有大小为3、5、6、7、9、10、11、12、20、24等非2的幂次的自定义类型、数组类型,全部可以触发这个条件。
这段判断的设计目的
MSVC在x86平台的默认cdecl调用约定中,对变参函数的参数传递做了分支优化:如果参数类型大小不超过8字节、且大小是2的整数次幂,就直接按值在栈上传递,va_arg取参时直接按对应大小偏移栈指针即可;剩下两类情况(大小超过8字节,或者大小不是2的幂、对齐规则无法直接适配自然栈块),会按指针方式传递,va_arg需要走间接寻址的分支取参数,这就是这个三元判断的存在意义。
内容的提问来源于stack exchange,提问作者Jonathan Woolf
相关产品推荐
相关产品推荐

