Подтвердить что ты не робот

Имеет ли C правило одного определения, такое как С++?

Недавно я узнал, что есть некоторые случаи, которые будут абсолютно нарушать ODR С++, но будут скомпилированы ОК в компиляторе C.

Например, этот wierd сценарий (со мной):

Источник 1

int var_global=-3;

Источник 2

#include <stdio.h>
#include <conio.h>

unsigned int var_global;

int main() {    

 printf("%d \n",var_global);
 getch();
 return 0;

}

У меня напечатанный результат -3 (хотя в Источник 2 var_global есть unsigned), и нет ошибки в переопределенной var_global.

Я знал, что C имеет разные правила с С++, но я не думаю, что это так отличается.

У меня есть google и читаю много результатов, но нет официального результата как этот на С++.

Итак, вопрос:

Имеет ли C правило одного определения, например С++?

и

Что это называется официально?

Мне нужно, чтобы он сравнивался с правилом С++, чтобы я мог глубже понять оба языка.

p/s: я использовал Visual Studio 2010 для компиляции кода выше.

4b9b3361

Ответ 1

Я думаю, что то, что вы ищете, - это глава §6.2.7 из стандарта C11, тип совместимого и составного типа (выделение мое)

Все объявления, относящиеся к одному и тому же объекту или функции, должны иметь совместимый тип; в противном случае поведение undefined.

и связанных с совместимым типом,

Два типа имеют совместимый тип, если их типы одинаковы.

В вашем случае int и unsigned int не являются совместимыми типами. Следовательно, undefined поведение.

Чтобы добавить немного ясности, в вашем источнике 2 unsigned int var_global; - это объявление, и оно не соответствует другому объявлению (и определению), поэтому это UB.

Тем не менее, утверждение вроде

 printf("%d \n",var_global);

всегда будет считать аргумент %d типом int. Если тип и спецификатор формата не совпадают, вы снова вызываете undefined поведение.


EDIT:

После редактирования ответ получится, используйте -fno-common, чтобы получить желаемую ошибку. (пропустил extern, с чем вас беспокоит, я считаю).

Цитата из онлайн-справочника GCC,

-fno-common

В коде C управляет размещением неинициализированных глобальных переменных. Компиляторы Unix C традиционно допускали множественные определения таких переменных в разных единицах компиляции, помещая переменные в общий блок. Это поведение, заданное как -fcommon, и является значением по умолчанию для GCC для большинства целей.. С другой стороны, это поведение не требуется по ISO C, а на некоторых объектах может нести ограничение скорости или размера кода на переменные ссылки. Параметр -fno-common указывает, что компилятор должен размещать неинициализированные глобальные переменные в разделе данных объектного файла, а не генерировать их как общие блоки. Это означает, что если одна и та же переменная объявлена ​​(без extern) в двух разных компиляциях, вы получаете ошибку с множественным определением при связывании. В этом случае вы должны скомпилировать с помощью - Вместо этого. Компиляция с -fno-common полезна для целей, для которых она обеспечивает лучшую производительность, или если вы хотите убедиться, что программа будет работать в других системах, которые всегда обрабатывают неинициализированные объявления переменных таким образом.


Я не знаю ни одного упоминания о формулировках "одного правила определения" в стандарте C, но по линии вы можете посмотреть приложение "J.5.11", "Несколько внешних определений",

Может быть более одного внешнего определения для идентификатора объекта, с или без явного использования ключевого слова extern; если определения не совпадают или больше, чем один инициализирован, поведение undefined.

Ответ 2

То, что вы видите, не имеет никакого отношения к правилу с одним определением. Это связано с тем, что %d ожидает знаковое значение и, следовательно, почти наверняка будет рассматривать его как знаковое значение в вашей реализации.

Однако это не то, на что вы должны положиться. В соответствии со стандартом C 7.19.6.1 The fprintf function /9 (я ссылаюсь на C99, но C11 почти то же самое в терминах аспектов, показанных здесь):

Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение undefined.

Поскольку вы используете поведение undefined, реализация может делать все, что захочет. Кроме того, в стандарте также конкретно указывается, что это поведение undefined, если (из Приложения J):

две декларации одного и того же объекта или функции указывают типы, которые несовместимы.

В вашем случае эти два объявления задают один и тот же объект, поскольку оба они имеют внешнюю связь.

Теперь вы можете подумать, что подписанные и беззнаковые целые числа будут совместимы, но вы ошибаетесь: 6.2.7 Compatible and composite type и 6.2.5 Types дает понять, что подписанные и неподписанные варианты несовместимы:

Два типа имеют совместимый тип, если их типы одинаковы.

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