while cleaning an old program GCC returned a warning that I could not understand. Here is a snippet:
#include <stdio.h>
#include <stdint.h>
extern int *foo[];
int main(void) {
if (foo != NULL)
printf("Foo is not null\n");
return 0;
}
$ gcc -Wall main.c
main.c: In function 'main':
main.c:7:17: warning: the comparison will always evaluate as 'true' for the address of 'foo' will never be NULL [-Waddress]
7 | if (foo != NULL)
| ^~
main.c:4:13: note: 'foo' declared here
4 | extern int *foo[];
|
I then tried to change the prototype to extern int **foo;:
$ gcc -Wall main.c
/usr/bin/ld: /tmp/cciux1Df.o: warning: relocation against `foo' in read-only section `.text'
As demonstrated here, without initialisation they should be considered the same.
Why **foo (rightfully) fails to compile as an undefined reference, while GCC successfully compiles with *foo[]?
What does the error relocation against 'foo' in read-only section '.text' really means as a warning?
Thanks.
The compiler tries to say you that this statement
if (foo != NULL)
does not make sense because an array always occupies memory and you declared an array
extern int *foo[];.
In the if statement the array designator foo is implicitly converted to pointer of the type int ** that points to the first element of the array (that is to the extent of memory occupied by the array). So it can not be equal to NULL.
Pay attention to that you should define the array somewhere in this or other translation unit. Otherwise the linker can issue an error that the array is not defined. Because the above declaration of the array is not its definition and not its tentative definition.
It is possible this test was used as a form of weak linking. The program might have been linked with a definition for the array in some cases, while in others the linker was made to assign a zero value to the symbol (a zero value for a linker symbol corresponds to a zero address for the array in C).
This behavior would not be defined by the C standard, but this old code may not have been written to be standard-conforming code, and this may have worked with the compiler and the linker used at the time.
As demonstrated here, without initialisation they should be considered the same.
That is a C++ question in the context of array parameters. You should not use it in reference to this issue.
Why
**foo(rightfully) fails to compile as an undefined reference, while GCC successfully compiles with*foo[]?
With extern int *foo[];, the foo in foo != NULL is converted to the address of its first element, and the compiler assumes that can never be null, so it optimizes foo != NULL to “true”. In consequence, the generated object code does not contain any reference to foo, because optimization removes it.
With extern int **foo;, the foo in foo != NULL requires getting the value stored in foo to test whether it is a null pointer, so the generated object code attempts to load data from foo. Then the linker is unable to satisfy that reference to foo, so it complains.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With