Внешняя связь const в C

Я играл с ключевым словом extern в C, когда столкнулся с этим странным поведением. У меня есть два файла:

файл1.с

#include<stdio.h>
int main()
{
    extern int a;
    a=10;
    printf("%d",a);
    return 0;
}

файл2.с

const int a=100;

Когда я компилирую эти файлы вместе, ошибок или предупреждений нет, и когда я их запускаю, вывод становится 10. Я ожидал, что компилятор сообщит об ошибке в строке a=10;.

Более того, если я изменю содержимое файла file2.c на

const int a;

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

Почему происходит это явление? Классифицируется ли это как неопределенное поведение? Это зависит от компилятора или машины?

PS: я видел много вопросов, связанных с этим, но они либо для C++, либо обсуждают только extern.


person skrtbhtngr    schedule 29.10.2016    source источник
comment
Проверьте это: stackoverflow.com/a/28734780/4085019   -  person PseudoAj    schedule 29.10.2016
comment
Это очень хороший ответ, но он не отвечает на мой вопрос.   -  person skrtbhtngr    schedule 29.10.2016


Ответы (4)


Компиляция и компоновка — это два разных этапа. Во время компиляции отдельные файлы компилируются в объектные файлы. Компилятор обнаружит, что файлы file1.c и file2.c внутренне непротиворечивы. На этапе компоновки компоновщик просто укажет все вхождения переменной a на одно и то же место в памяти. По этой причине вы не видите ошибок компиляции или компоновщика.

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

Следующий stackoverflow также говорит о том, что компоновщик не может выполнять проверку типов для внешних переменных.

Есть ли проверка типов в компоновщиках C или C++?< /а>

Точно так же типы глобальных переменных (и статические члены классов и т. д.) не проверяются компоновщиком, поэтому, если вы объявите extern int test; в одной единице перевода и определить поплавковый тест; в другом вы получите плохие результаты.

person Jay Rajput    schedule 29.10.2016

Это поведение undefined, но компилятор вас не предупредит. Как это могло быть? Он понятия не имеет, как вы объявляете переменную в другом файле.

Попытка изменить переменную, объявленную const, является поведением undefined. Возможно (но не обязательно), что переменная будет храниться в постоянной памяти.

person rici    schedule 29.10.2016

Это известное поведение компиляторов C. Это одно из различий между C и C++, где применяется строгая проверка типов во время компиляции. Ошибка сегментации возникает при попытке присвоить значение константе, потому что компоновщик помещает значения константы в доступный только для чтения сегмент elf, а запись в этот адрес памяти является ошибкой времени выполнения (сегментации). но во время компиляции компилятор не проверяет никаких «внешних», а компоновщик C не проверяет типы. поэтому он проходит компиляцию/связку.

person Yanir    schedule 29.10.2016
comment
Янир, у вас есть предложение «Это одно из различий между C и C++, где применяется строгая проверка типов во время компиляции». Вы хотели сказать, что компилятор С++ поймал бы эту проблему? Поскольку файлы разные, они могут быть скомпилированы отдельно в объектные файлы. - person Jay Rajput; 29.10.2016
comment
Компилятор @JayRajput C ++ не уловил бы этого, но компоновщик смог бы. - person user4581301; 29.10.2016
comment
@ user4581301, кажется, в Интернете есть запутанная информация. Например: в этой статье stackoverflow предполагается, что компоновщик не будет выполнять проверку типов для внешних переменных. stackoverflow.com/questions/28090854/. У вас есть источник, который доказывает, что компоновщик выполняет проверку типов для внешних переменных? Я хотел бы понять больше об этом. - person Jay Rajput; 29.10.2016
comment
@JayRajput Мне придется прочитать стандартное чтение, чтобы найти точную формулировку. Вопрос, который вы связали, пытается ответить как для C, так и для C++, и они очень, очень разные звери, когда дело доходит до проверки типов. См. комментарий Якка и ответ Джоша Келли. - person user4581301; 29.10.2016
comment
@JayRajput Похоже, это работает немного не так, как я думал. Согласно Basic.Link, тип const имеет внутреннюю связь и не может быть виден за пределами текущего файла, если явно не объявлен как extern. Таким образом, const int x = 0; не может быть externed, а extern const int x = 0; может быть externed. Я не могу найти никакой информации о том, перехватывается ли отсутствующий const компоновщиком. GCC 4.8 и 6.1, похоже, не ловят, поэтому на данный момент я предполагаю, что это не требуется. - person user4581301; 29.10.2016
comment
И теперь, когда я знаю, какие ключевые слова искать, Почему делает «extern const int n;» не работает должным образом? Хороший образовательный день. Теперь вернемся к работе по уборке дома. - person user4581301; 29.10.2016

Ваша программа вызывает неопределенное поведение без необходимости диагностики (независимо от того, имеет ли const int a инициализатор). Соответствующий текст в C11 — 6.2.7/2:

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

Также 6.2.2/2:

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

В C const int a = 100; означает, что a имеет внешнюю связь. Таким образом, он обозначает тот же объект, что и extern int a;. Однако эти два объявления имеют несовместимый тип (int несовместим с const int, см. определение «совместимого типа» в 6.7.2).

person M.M    schedule 30.10.2016