cl x64: unsigned long outside / inside union: error C2099: initializer is not a constant / NO 错误
cl x64: unsigned long outside / inside union: error C2099: initializer is not a constant / NO error
案例一. 文件: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)不产生任何错误,不产生警告。
更新。请注意:问题不是关于 safe code / unsafe code
或 wrong code / right code
。问题是关于 cl
编译器行为。 IE。为什么在第二种情况下 cl 认为 initializer IS a constant
(这样的结论是由于没有错误消息)。
静态存储持续时间对象的初始化器必须是常量表达式。
在 C89 标准(Microsoft C conforms 的唯一标准)中,常量表达式中不允许从指针转换为整数。
这是语义规则而不是约束,这意味着程序具有未定义的行为,不需要诊断。因此,允许编译器拒绝该程序(但不需要拒绝它),它可能会也可能不会发出诊断,并且不需要在各种未定义的程序之间保持一致性。
我们不能从没有错误消息中得出结论认为初始值设定项已被接受为常量。
从 C99 开始,该标准还包括文本 "An implementation may accept other forms of constant expressions"。编译器应该发布一致性文档,列出它接受哪些表达式作为常量,尽管我找不到 MSVC 的文档。 (如果可以请留下评论!)
注意有关将指针转换为 unsigned long
的规则也可能是相关的。来自最新标准:
Any pointer type may be converted to an integer type. Except as previously specified, the
result is implementation-defined. If the result cannot be represented in the integer type,
the behavior is undefined. The result need not be in the range of values of any integer
type.
因此,即使 C99 或 C11 兼容的编译器记录它接受从常量表达式中的指针到整数的转换,转换的结果仍然可能导致启动时陷入陷阱,或导致未定义的行为 (和以前一样,这意味着不需要诊断并且程序可能会被拒绝)。
案例一. 文件: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)不产生任何错误,不产生警告。
更新。请注意:问题不是关于 safe code / unsafe code
或 wrong code / right code
。问题是关于 cl
编译器行为。 IE。为什么在第二种情况下 cl 认为 initializer IS a constant
(这样的结论是由于没有错误消息)。
静态存储持续时间对象的初始化器必须是常量表达式。
在 C89 标准(Microsoft C conforms 的唯一标准)中,常量表达式中不允许从指针转换为整数。
这是语义规则而不是约束,这意味着程序具有未定义的行为,不需要诊断。因此,允许编译器拒绝该程序(但不需要拒绝它),它可能会也可能不会发出诊断,并且不需要在各种未定义的程序之间保持一致性。
我们不能从没有错误消息中得出结论认为初始值设定项已被接受为常量。
从 C99 开始,该标准还包括文本 "An implementation may accept other forms of constant expressions"。编译器应该发布一致性文档,列出它接受哪些表达式作为常量,尽管我找不到 MSVC 的文档。 (如果可以请留下评论!)
注意有关将指针转换为 unsigned long
的规则也可能是相关的。来自最新标准:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
因此,即使 C99 或 C11 兼容的编译器记录它接受从常量表达式中的指针到整数的转换,转换的结果仍然可能导致启动时陷入陷阱,或导致未定义的行为 (和以前一样,这意味着不需要诊断并且程序可能会被拒绝)。