The program below compiles without errors.
#include <stdio.h>
char addr_a[8];
char addr_b[8];
unsigned long my_addr = (unsigned long)addr_b - 8; // PASS
// unsigned long my_addr = (unsigned long)addr_b - (unsigned long)addr_a; // FAIL (error: initializer element is not constant)
int main() {
printf("%lx\n", my_addr);
return 0;
}
Interestingly, when I set unsigned long my_addr = (unsigned long)addr_b - (unsigned long)addr_a
the compiler throws "error: initializer element is not constant."
I know globals can only be initialized with a constant expression. I also know that the types of constant expressions that can be used in an initializer for a global are specified in section 6.6p7 of the C standard:
More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
- an arithmetic constant expression,
- a null pointer constant,
- an address constant, or
- an address constant for a complete object type plus or minus an integer constant expression.
Note that an address constant minus an integer constant is allowed, but not an address constant minus another address constant.
Question:
Why does the C standard restrict the ways you can initialize global variables? What is stopping the C standard from accepting unsigned long my_addr = (unsigned long)addr_b - (unsigned long)addr_a
?
Why would you want this?
Suppose addr_a
and addr_b
represent the start and end of the .text
section respectively. A program may want to map the .text
section, which has size (unsigned long)addr_b - (unsigned long)addr_a
. The trusted-firmware-a project does this in Boot Loader stage 2 (BL2). See BL_CODE_END - BL_CODE_BASE
, which is used in arm_bl2_setup.c.