I was watching this c lection (it's in russian). At around 16:10 the lector asked an open question:
Having this code:
int* foo()
{
volatile auto a = nullptr;
int* b = a;
return b;
}
int main()
{}
Clang generates the following assembly for foo
(-Ofast)
mov qword ptr [rsp - 8], 0 # volatile auto a = nullptr;
xor eax, eax
ret
Meaning the compiler assumes there is no side effect for reading from a
and basically removes int* b = a;
part of the code.
GCC on the other hand generates a bit different code
mov QWORD PTR [rsp-8], 0 # volatile auto a = nullptr;
mov rax, QWORD PTR [rsp-8] # int* b = a;
xor eax, eax
ret
Here compiler believes reading from a
does produce the side effect and leaves everything as is.
The question is what is the correct behaviour according to C 20 standard?
CodePudding user response:
The type of a
will be volatile std::nullptr_t
.
std::nullptr_t
has no (or at least is not required to have any) internal state. It could behave like e.g. an empty class. Only the conversion behavior is specified for this type.
There is no need for std::nullptr_t
to occupy any specific amount of memory (except the one byte to assure unique addresses) or to store an value in the memory it occupies. So there doesn't need to be a 0
there to read. The assignment int* b = a;
does not depend on any state or value stored in a
, only on its type.
I would say both behaviors are correct. What exactly the meaning of the observable side effect of volatile
access is is anyway implementation-defined and if std::nullptr_t
has the layout of an empty class, then one wouldn't expect any instruction loading/storing from/to it to be generated for the initialization and the implicit conversion in int* b = a;
anyway. But if std::nullptr_t
is implemented similar to a pointer which always has value 0
, then it would be reasonable to expect a store and load.