问题
Case 1. File: test1.c
:
unsigned long val = (unsigned long)&"test";
int main()
{
return 0;
}
Compiler invocation: cl test1.c /c
Results:
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
Case 2. File: test2.c
:
union { unsigned long val; } val = { (unsigned long)&"test" };
int main()
{
return 0;
}
Compiler invocation: cl test2.c /c
Results:
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'
Question: why after putting unsigned long
into union
(case 2, file test2.c) the error C2099: initializer is not a constant
is gone?
Notes:
for both code versions
cl x86
(same version as for x64) produces NO errors, NO warnings.for both code versions
gcc x86/x64
(version 9.3.0) produces NO errors, NO warnings.
UPD. Please note: the question is not about safe code / unsafe code
or wrong code / right code
. The question is about cl
compiler behavior. I.e. why in the 2nd case the cl considers that initializer IS a constant
(such conclusion is from the absence of the error message).
回答1:
Initializers for objects of static storage duration must be constant expressions .
In the C89 standard (the only standard to which Microsoft C conforms) , a cast from pointer to integer is not permitted in a constant expression.
This is a Semantic rule and not a Constraint, which means the program has undefined behaviour with no diagnostic required. Therefore the compiler is permitted to reject the program (but is not required to reject it), it may or may not issue diagnostics, and there is no requirement of consistency amongst various programs that are undefined.
We cannot conclude from the absence of an Error message that the initializer was accepted as a constant .
Since C99, the standard also includes the text "An implementation may accept other forms of constant expressions". The compiler should publish conformance documentation listing which expressions it accepts as constants, although I couldn't find that documentation for MSVC. (leave a comment if you can!)
It may also be relevant to note the rules about casting pointer to unsigned long
. From the latest Standard:
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.
So, even if the C99 or C11-compliant compiler documents that it accepts casts from pointer to integer in constant expressions, it may still be that the result of the cast causes a trap on startup, or causes undefined behaviour (which , as before, means no diagnostic is required and the program may be rejected).
来源:https://stackoverflow.com/questions/62160814/cl-x64-unsigned-long-outside-inside-union-error-c2099-initializer-is-not-a