Прототип внешней переменной: сравнение массива, не равного нулевому указателю, всегда верно

при очистке старой программы 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'
  1. Как показано здесь, без инициализации их следует считать одинаковыми. Почему **foo (правильно) не компилируется как неопределенная ссылка, а GCC успешно компилируется с *foo[]?

  2. Что на самом деле означает ошибка relocation against 'foo' in read-only section '.text' как предупреждение?

Спасибо.

foo — это массив, а не указатель. Массивы никогда не могут быть нулевыми.
Barmar 31.01.2023 19:32

Вам нужно связать с объектным файлом, который определяет foo.

Barmar 31.01.2023 19:36

@Barmar как это массив, а не указатель на массив?

Adhrill 31.01.2023 19:39

Потому что объявление заканчивается на []. Массивы и указатели эквивалентны только в объявлениях параметров функций, а не где-либо еще.

Barmar 31.01.2023 19:39

@Бармар, спасибо! Я не знал этого различия.

Adhrill 31.01.2023 19:47
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
3
5
69
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Компилятор пытается сказать вам, что этот оператор

if (foo != NULL)

не имеет смысла, потому что массив всегда занимает память, а вы объявили массив

extern int *foo[];.

В операторе if указатель массива foo неявно преобразуется в указатель типа int **, который указывает на первый элемент массива (то есть в пределах памяти, занимаемой массивом). Так что он не может быть равен NULL.

Обратите внимание, что вы должны определить массив где-то в той или иной единице перевода. В противном случае компоновщик может выдать ошибку, что массив не определен. Потому что приведенное выше объявление массива не является его определением и не является его предварительным определением.

Почему foo неявно преобразуется в int ** в операторе if, а не во время его объявления?

Adhrill 31.01.2023 20:02

Обозначение массивов @Adhrill неявно преобразуется в указатели на их первые элементы в выражениях с редкими исключениями, когда, например, используется в качестве операндов оператора sizeof. Декларации есть декларации. Конверсии не применяются.

Vlad from Moscow 31.01.2023 20:09

Я снова подумал об этом, почему массив выделяется даже с ключевым словом extern? Я предположил, что это означает, что компоновщик должен найти «настоящее» объявление/выделение и не сможет связать.

Adhrill 01.02.2023 08:44

@Adhrill Это объявление массива, а не его определение. Но компоновщики могут вести себя по-разному и могут определять массив, хотя это не предварительное определение.

Vlad from Moscow 01.02.2023 10:14

Возможно, этот тест использовался как форма слабой связи. В некоторых случаях программа могла быть связана с определением массива, в то время как в других компоновщик был вынужден присваивать символу нулевое значение (нулевое значение для символа компоновщика соответствует нулевому адресу для массива в C). .

Таким образом, поведение не будет определяться стандартом C, но этот старый код, возможно, не был написан в соответствии со стандартом, и это могло работать с компилятором и компоновщиком, использовавшимся в то время.

Как показано здесь, без инициализации их следует считать одинаковыми.

Это вопрос С++ в контексте параметров массива. Вы не должны использовать его в отношении этой проблемы.

Почему **foo (правильно) не компилируется как неопределенная ссылка, а GCC успешно компилируется с *foo[]?

С extern int *foo[];foo в foo != NULL преобразуется в адрес своего первого элемента, и компилятор предполагает, что он никогда не может быть нулевым, поэтому он оптимизирует foo != NULL до «истины». Как следствие, сгенерированный объектный код не содержит никакой ссылки на foo, потому что оптимизация удаляет его.

С extern int **foo;foo в foo != NULL требуется получить значение, хранящееся в foo, чтобы проверить, является ли он нулевым указателем, поэтому сгенерированный объектный код пытается загрузить данные из foo. Тогда компоновщик не может удовлетворить эту ссылку на foo, поэтому он жалуется.

Другие вопросы по теме