【发布时间】:2020-09-21 10:35:59
【问题描述】:
案例1.文件:test1.c:
unsigned long val = (unsigned long)&"test";
int main()
{
return 0;
}
编译器调用:cl test1.c /c
结果:
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28611 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
test1.c
test1.c(1): warning C4311: 'type cast': pointer truncation from 'char (*)[5]' to 'unsigned long'
test1.c(1): error C2099: initializer is not a constant
案例2.文件:test2.c:
union { unsigned long val; } val = { (unsigned long)&"test" };
int main()
{
return 0;
}
编译器调用:cl test2.c /c
结果:
Microsoft (R) C/C++ Optimizing Compiler Version 19.25.28611 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
test2.c
test2.c(1): warning C4311: 'type cast': pointer truncation from 'char (*)[5]' to 'unsigned long'
问题:为什么将unsigned long 放入union(案例2,文件test2.c)后error C2099: initializer is not a constant 消失了?
注意事项:
对于两个代码版本
cl x86(与 x64 的版本相同)不会产生错误和警告。对于两个代码版本
gcc x86/x64(版本 9.3.0)都不会产生错误和警告。
UPD。请注意:问题与safe code / unsafe code 或wrong code / right code 无关。问题是关于cl 编译器行为。 IE。为什么在第二种情况下,cl认为initializer IS a constant(这样的结论是由于没有错误消息)。
【问题讨论】:
-
你做错了。最重要的是,因为 Microsoft Visual Studio 编译器将
long(因此unsigned long)保留为 32 位类型。在 64 位系统上,指针是 64 位宽的,它不能放入 32 位变量中。 -
是什么让您认为尝试将指针值塞入
unsigned long是安全的? -
@Someprogrammerdude:C 标准明确允许从指针到整数类型的转换,即使目标太窄而无法表示完整的地址信息。结果是实现定义的,可用于例如对齐问题。无论如何,代码是否“错误”不是问题。编译器行为的区别是。
-
@AndrewHenle:OP 给了我们一个关于 MRE、编译器版本识别和准确编译器消息的恰当问题。该问题证明了对语言问题的认识,并且确实提出了一个关于编译器行为的重要问题。 OP 可能已将其他代码减少到此 MRE,并且问题的上述质量表明他们也知道转换警告的含义。他们应该得到怀疑的好处,即他们知道自己在做什么,不应该对代码中的问题进行下意识的挑剔,这些问题可能是由于是 MRE 而不是完整的真实代码。
标签: c initialization long-integer unions cl