In C++, are the following two declarations equivalent?
static volatile uint16_t *ADCReadings = (uint16_t[64]){0};
or
static volatile uint16_t ADCReadings[64] = {0};
I'm reserving space for a buffer used inside and outside an ISR on AVR. But, though it would be interesting, I'm not looking here to know if this is the best way to do it - I'm wondering what the differences (if any) between the two declarations are, so that I can understand this better.
I do know that the two different declarations produce different sized binaries, so the compiler appears to be treating them differently.
Are the following two declarations equivalent?
No. (uint16_t[64]){0}
is a temporary array (a compound literal*), and if you try to compile the first line, you'll get a self-explanatory warning/error:
warning: temporary whose address is used as value of local variable 'ADCReadings' will be destroyed at the end of the full-expression
(Clang)
error: taking address of temporary array
(Gcc)
So, in the first line ADCReadings
becomes a dangling pointer. Using it as a pointer into a buffer invokes undefined behaviour.
Gcc manual reads:
In C, a compound literal designates an unnamed object with static or automatic storage duration. In C++, a compound literal designates a temporary object that only lives until the end of its full-expression. As a result, well-defined C code that takes the address of a subobject of a compound literal can be undefined in C++, so G++ rejects the conversion of a temporary array to a pointer.
Is this true even at global scope?
Gcc and Clang don't produce warnings/errors in this case. Gcc manual further reads:
As an optimization, G++ sometimes gives array compound literals longer lifetimes: when the array either appears outside a function or has a const-qualified type. ... if
foo
were a global variable, the array would have static storage duration. But it is probably safest just to avoid the use of array compound literals in C++ code.
So it seems that at the global scope ADCReadings
won't be dangling.
I do know that the two different declarations produce different sized binaries
In the first case, the compound literal array goes into the .bss
section, and ADCReadings
goes into the .data
section:
0000000000004040 l O .bss 0000000000000080 ._0
0000000000004010 g O .data 0000000000000008 ADCReadings
ADCReadings
is a pointer to ._0
.
In the second case, ADCReadings
goes into the .bss
directly:
0000000000004040 g O .bss 0000000000000080 ADCReadings
This also translates into how ADCReadings
is used. The following simple function:
uint16_t* foo() {
return ADCReadings;
}
compiles into:
push rbp
mov rbp,rsp
mov rax,QWORD PTR [rip+0x2ed8] # 4010 <ADCReadings>
pop rbp
ret
and
push rbp
mov rbp,rsp
lea rax,[rip+0x2f08] # 4040 <ADCReadings>
pop rbp
ret
* Compound literals are not part of the standard C++, some compilers (Gcc, Clang) allow them as an extension.