при очистке старой программы GCC вернул предупреждение, которое я не мог понять. Вот фрагмент:
#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[];
|
Затем я попытался изменить прототип на extern int **foo;
:
$ gcc -Wall main.c
/usr/bin/ld: /tmp/cciux1Df.o: warning: relocation against `foo' in read-only section `.text'
Как показано здесь, без инициализации их следует считать одинаковыми.
Почему **foo
(правильно) не компилируется как неопределенная ссылка, а GCC успешно компилируется с *foo[]
?
Что на самом деле означает ошибка relocation against 'foo' in read-only section '.text'
как предупреждение?
Спасибо.
Вам нужно связать с объектным файлом, который определяет foo
.
@Barmar как это массив, а не указатель на массив?
Потому что объявление заканчивается на []
. Массивы и указатели эквивалентны только в объявлениях параметров функций, а не где-либо еще.
@Бармар, спасибо! Я не знал этого различия.
Компилятор пытается сказать вам, что этот оператор
if (foo != NULL)
не имеет смысла, потому что массив всегда занимает память, а вы объявили массив
extern int *foo[];
.
В операторе if указатель массива foo
неявно преобразуется в указатель типа int **
, который указывает на первый элемент массива (то есть в пределах памяти, занимаемой массивом). Так что он не может быть равен NULL
.
Обратите внимание, что вы должны определить массив где-то в той или иной единице перевода. В противном случае компоновщик может выдать ошибку, что массив не определен. Потому что приведенное выше объявление массива не является его определением и не является его предварительным определением.
Почему foo
неявно преобразуется в int **
в операторе if, а не во время его объявления?
Обозначение массивов @Adhrill неявно преобразуется в указатели на их первые элементы в выражениях с редкими исключениями, когда, например, используется в качестве операндов оператора sizeof. Декларации есть декларации. Конверсии не применяются.
Я снова подумал об этом, почему массив выделяется даже с ключевым словом extern
? Я предположил, что это означает, что компоновщик должен найти «настоящее» объявление/выделение и не сможет связать.
@Adhrill Это объявление массива, а не его определение. Но компоновщики могут вести себя по-разному и могут определять массив, хотя это не предварительное определение.
Возможно, этот тест использовался как форма слабой связи. В некоторых случаях программа могла быть связана с определением массива, в то время как в других компоновщик был вынужден присваивать символу нулевое значение (нулевое значение для символа компоновщика соответствует нулевому адресу для массива в C). .
Таким образом, поведение не будет определяться стандартом C, но этот старый код, возможно, не был написан в соответствии со стандартом, и это могло работать с компилятором и компоновщиком, использовавшимся в то время.
Как показано здесь, без инициализации их следует считать одинаковыми.
Это вопрос С++ в контексте параметров массива. Вы не должны использовать его в отношении этой проблемы.
Почему
**foo
(правильно) не компилируется как неопределенная ссылка, а GCC успешно компилируется с*foo[]
?
С extern int *foo[];
foo
в foo != NULL
преобразуется в адрес своего первого элемента, и компилятор предполагает, что он никогда не может быть нулевым, поэтому он оптимизирует foo != NULL
до «истины». Как следствие, сгенерированный объектный код не содержит никакой ссылки на foo
, потому что оптимизация удаляет его.
С extern int **foo;
foo
в foo != NULL
требуется получить значение, хранящееся в foo
, чтобы проверить, является ли он нулевым указателем, поэтому сгенерированный объектный код пытается загрузить данные из foo
. Тогда компоновщик не может удовлетворить эту ссылку на foo
, поэтому он жалуется.
foo
— это массив, а не указатель. Массивы никогда не могут быть нулевыми.