Вопрос

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

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 отказаться быть связанными друг с другом?

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

Это было полезно?

Решение

Смотрите также Что такое внешние переменные в C.Это упоминается в стандарте 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

РЕДАКТИРОВАТЬ:Вывод NM для файла объекта, скомпилированного из «Extern int x;»; где x фактически используется в одной из функций

00000000 T _main
         U _unknown
         U _x
         U dyld_stub_binding_helper
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top