使用指针算法计算类型大小的替代方法
以下代码是100%可移植的吗?
int a=10; size_t size_of_int = (char *)(&a+1)-(char*)(&a); // No problem here? std::cout<<size_of_int;// or printf("%zu",size_of_int);
PS :问题仅限于学习目的。 所以请不要给出像Use sizeof()
等的答案
从ANSI-ISO-IEC 14882-2003,p.87(c ++ 03):
“75)接近指针运算的另一种方法是首先将指针转换为字符指针:在此方案中,首先将转换指针中添加或减去的表达式的整数值乘以最初指向的对象,结果指针被转换回原始类型。对于指针减法,字符指针之间差异的结果同样除以最初指向的对象的大小。
这似乎表明指针差异等于对象大小。
如果我们删除UB’ness从将指针递增到标量a并将a转换为数组:
int a[1]; size_t size_of_int = (char*)(a+1) - (char*)(a); std::cout<
然后这看起来没问题。 如果对齐要求始终可以被对象的大小整除,则有关对齐要求的条款与脚注一致。
更新:有趣。 正如大多数人可能知道的那样,GCC允许指定类型的显式对齐作为扩展。 但我不能用它破坏OP的“sizeof”方法,因为GCC拒绝编译它:
#include typedef int a8_int __attribute__((aligned(8))); int main() { a8_int v[2]; printf("=>%d\n",((char*)&v[1]-(char*)&v[0])); }
消息是error: alignment of array elements is greater than element size
。
根据C ++标准5.7 / 5, &a+1
将导致未定义的行为:
当一个具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。 <...>如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不应产生溢出; 否则,行为未定义 。
根据5.7 / 4, &a+1
可以:
出于这些运算符的目的,指向非arrays对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型。
这意味着5.7 / 5可以在没有UB的情况下应用。 最后在5.7 / 6中评论了75, @Luther Blissett
在他的回答中指出,问题中的代码是有效的。
在生产代码中,您应该使用sizeof
。 但是C ++标准并不保证sizeof(int)
会在每个32位平台上产生4。
不会。这段代码不会像您期望的那样在每个平台上运行。 至少在理论上,可能存在具有例如24位整数(= 3字节)但32位对齐的平台。 对于(较旧或较简单)平台而言,这种对齐不是非典型的。 然后,您的代码将返回4,但sizeof(int)将返回3。
但我不知道一个真正的硬件行为那样。 实际上,您的代码将适用于大多数或所有平台。
由于以下原因,它不是100%便携式的:
- 编辑:你最好使用
int a[1];
然后a+1
变得明确有效。 - &a在寄存器存储类的对象上调用未定义的行为。
- 如果对齐限制大于或等于
int
类型的大小,则size_of_int
将不包含正确的答案。
免责声明:
我不确定上面是否适用于C ++。
为什么不呢:
size_t size_of_int = sizeof(int);
它可能是实现定义的。
我可以想象一个(假设的)系统,其中sizeof(int)小于默认对齐。
看起来很安全地说size_of_int >= sizeof(int)
上面的代码将在目标平台上可移植地计算sizeof(int)
,但后者是实现定义的 – 您将在不同平台上获得不同的结果。
是的,它给你相当于sizeof(a)
但使用ptrdiff_t
而不是size_t
类型。
关于类似问题的辩论。
请参阅我对该问题的答案的评论,以获取一些指示,说明为什么这不仅是不可移植的,而且是标准的未定义行为。