Предположим, у меня есть два файла:
== File1 ==
extern char* foo;
== File2 ==
double foo;
Эти два файла, похоже, прекрасно компилируются и связываются как с g ++, так и clang ++, несмотря на несоответствие типов. Насколько я понимаю, рекомендуемая практика — помещать объявление extern в заголовок, который включает оба файла, чтобы File2 выдавал ошибку переопределения.
Мои вопросы:
Приводит ли это к неопределенному поведению в соответствии со стандартом c ++?
Ну, реальный вопрос заключается в том, неопределенное поведение или это указано стандартом как плохо формируется (на стандартном языке). Потому что, очевидно, это не правильно. Я пытался найти что-то из стандарта по этому поводу, но безрезультатно. Тем не менее, в ряде подобных ситуаций, например, при несовпадении decl / def или броске прикольных вещей в компоновщик (см. Раздел 3.5, 7.5 или в поиске «extern» или «linkage»), стандарт обычно заканчивается следующим образом:
Программа плохо сформирована, диагноз не требуется.
Так что, держу пари, вполне безопасно предположить, что это также и здесь. Это будет означать, что это ошибочный код, худший, чем «неопределенное поведение», поскольку UB часто будет иметь какое-то разумное поведение для конкретной реализации (хотя вы не должны спекулировать, каким будет это поведение, и, конечно, не полагаться на это спекуляции). Термин «неправильно сформированный» используется в стандарте очень свободно, и вы можете более или менее сделать вывод, что это означает, что код является FUBAR. Это также означало бы, что по стандарту компоновщик не требуется реализовывать таким образом, чтобы он мог отлавливать такого рода ошибки, и поэтому он правильно компилируется и связывается, но держится за носки при запуске ,
Могут ли линкеры улавливать такое несоответствие типов?
В теории да. Реализация компоновщика может кодировать (с изменением имени) тип переменной во внешний символ и, таким образом, иметь возможность либо ограничить связь с вещами, типы которых соответствуют (например, перегруженные функции), либо выдать диагноз (ошибка), когда встречается несоответствие типов. Я думаю, что первый будет слишком разрешительным по сравнению со стандартом.
Тем не менее, все известные мне компиляторы не искажают имена переменных, и, таким образом, можно предположить, что такое несоответствие является «неправильным, диагноз не требуется».
Приводит ли это к неопределенному поведению в соответствии со стандартом c ++?
определенно да.
Если нет, что входит в foo в File1?
Хм, он будет иметь значение ноль, так как double foo;
не установлен ни к чему. Но так как вы не можете полагаться на нулевое значение для double, оно фактически равно NULL для указателя, поэтому невозможно сказать, что произойдет, если вы попытаетесь как-то сравнить foo
как указатель с foo
как двойной.
Естественно, если указатель foo
назначается, например, foo = malloc(100);
затем двойная foo
будет содержать биты указателя, который, скорее всего, не является особенно хорошим числом с плавающей запятой — вполне возможно, является недопустимым в 64-битной системе, поскольку верхние несколько битов, скорее всего, будут равны нулю, что означает, что значение равен нулю, и в этом случае остальные биты также должны быть равны нулю. Хотя это зависит от внутреннего формата с плавающей запятой и фактического значения указателя
Могут ли линкеры улавливать такое несоответствие типов?
Нет, имена для переменных не «искажены» для типа, даже в C ++. Только функции имеют типы функций, закодированные в имени функции.
Технически, компоновщик или некоторый компонент компилятора МОЖЕТ отследить, какой тип представляет символ, а затем выдать ошибку или предупреждение. Но от стандарта не требуется этого делать. Вы обязаны делать правильные вещи.