Предварительные определения в C99 и ссылки

Рассмотрим программу на С, состоящую из двух файлов:

f1.c:

int x;

f2.c:

int x=2;

Мое прочтение пункта 6.9.2 стандарта C99 заключается в том, что эта программа должна быть отклонена. В моей интерпретации 6.9.2 переменная x предварительно определено в f1.c, но это предварительное определение становится фактическим определением в конце единицы перевода, и (на мой взгляд) должно вести себя так, как если бы f1.c содержал определение int x=0;,

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

Я сомневаюсь, что это происходит случайно или просто как "простая" функция, предоставляемая в дополнение к требованиям стандарта. Если вы думаете об этом, это означает, что в компоновщике есть специальная поддержка для тех глобальных переменных, которые не имеют инициализатора, в отличие от тех, которые явно инициализированы нулем. Кто-то сказал мне, что функция компоновщика может быть необходима для компиляции Фортрана в любом случае. Это было бы разумным объяснением.

Есть мысли по этому поводу? Другие интерпретации стандарта? Названия платформ, на которых файлы f1.c а также f2.c отказаться быть связанным вместе?

Примечание: это важно, потому что вопрос возникает в контексте статического анализа. Если два файла могут отказать в связывании на какой-либо платформе, анализатор должен подать жалобу, но если каждая платформа компиляции принимает это, то нет причин предупреждать об этом.

3 ответа

Решение

Смотрите также Что такое внешние переменные в Си. Это упоминается в стандарте C в информативном Приложении J как общее расширение:

J.5.11 Несколько внешних определений

Для идентификатора объекта может быть несколько внешних определений с явным использованием ключевого слова extern или без него; если определения не совпадают или более одного инициализированы, поведение не определено (6.9.2).

Предупреждение

Как указывает здесь @litb и как указано в моем ответе на вопрос с перекрестными ссылками, использование нескольких определений для глобальной переменной приводит к неопределенному поведению, которое является стандартом для выражения "все может произойти". Одна из вещей, которая может произойти, заключается в том, что программа ведет себя так, как вы ожидаете; и в J.5.11 примерно сказано: "Возможно, вам повезет чаще, чем вы заслуживаете". Но программа, которая опирается на несколько определений внешней переменной - с явным ключевым словом extern или без него - не является строго соответствующей программой и не гарантирует, что она будет работать везде. Эквивалентно: это содержит ошибку, которая может показать или не показать себя.

В стандарте есть нечто, называемое "общим расширением", где допускается многократное определение переменных, если переменная инициализируется только один раз. См. http://c-faq.com/decl/decldef.html

На связанной странице написано, что это относится к платформам Unix - я думаю, что это то же самое для c99 и c89- хотя, возможно, оно было принято многими компиляторами для формирования своего рода стандарта де-факто. Интересно.

Это уточнить мой ответ на комментарий от olovb:

вывод nm для объектного файла, скомпилированного из "int x;". На этой платформе к символам добавляется символ "_", то есть переменная x отображается как _x.

00000000 T _main
         U _unknown
00000004 C _x
         U dyld_stub_binding_helper

вывод nm для объектного файла, скомпилированного из "int x=1;"

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

вывод nm для объектного файла, скомпилированного из "int x=0;"

00000000 T _main
         U _unknown
000000a0 D _x
         U dyld_stub_binding_helper

вывод nm для объектного файла, скомпилированного из "extern int x;"

00000000 T _main
         U _unknown
         U dyld_stub_binding_helper

EDIT: вывод nm для объектного файла, скомпилированного из "extern int x;" где х фактически используется в одной из функций

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper
Другие вопросы по тегам