2016-05-09 7 views
3

Стандарты C11 говорят о связи идентификаторов, но нет очевидной дискуссии о правилах связывания единиц перевода. Мой вопрос воспитывается путем составления двух простых примеров с использованием clang.Каковы правила связывания единиц перевода в C11?

Вот мой первый пример, который имеет два объявления одной и ту же функцию, но с несовместимыми типами:

//testall.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

char myfun(char*c){ 
    return *c; 
} 

Затем я запустить команду: $ лязга -std = C11 testall.c
И звон отчетов Ошибка:

testall.c:9:10: error: conflicting types for 'myfun'
char myfun(char*c){
^
testall.c:2:17: note: previous declaration is here
extern char myfun(void*);
^
1 error generated.

Я понимаю эту ошибку, поскольку указатель на указатель и указатель на символ несовместимы.
Что меня смущает, что когда я разделить два объявления в двух разных единицах трансляции, а затем соединить их в одно целое, лязг не сообщает об ошибке:

//test.c 
extern char myfun(void*); 

int main(){ 
    char a='c'; 
    a=myfun(&a); 
} 

// mylib.c 
char myfun(char*c){ 
    return *c; 
} 

Затем я выполнить эту команду: $ лязг -std = c11 test.c mylib.c.
clang компилирует и связывает два блока перевода без сообщения об ошибке или предупреждении.

Я думал, что связывание двух единиц перевода следует правилам в разделе 6.2.2 Связи идентификаторов стандартов C11. Но, похоже, это не так. Может ли кто-нибудь помочь мне прояснить это?

+0

Просто потому, что вы не получаете диагностику, это не значит, что код является законным! Традиционно компилятор просматривает только одну единицу перевода за раз (поэтому он не смог обнаружить несоответствие при компиляции), а информация типа не передается в объектный файл, поэтому компоновщик не сможет обнаружить несоответствие или. –

+0

@NateEldredge Попробуйте сделать один файл за раз, а не указывая оба в командной строке. Выполнение 'gcc a.c b.c' выполняет компиляцию и соединение за один шаг. C не работает с именем, так что имена функции выглядят точно так же, как и для компоновщика. Теперь, что, если у вас есть три объектных файла с 'myfun'? Вы получите ошибку переопределения функции. Так ясно, что вы сказали, не может быть правдой. –

+0

Хотя 'gcc a.c b.c' запускает как компилятор, так и компоновщик, они представляют собой два отдельных прохода, которые не обмениваются информацией, отличной от объектного. Линкер способен обнаруживать несколько ** определений ** одного и того же символа. Но в вашем примере функция 'myfun' определяется ** только **, в' mylib.c'. Файл 'test.c' содержит ** декларацию **' myfun', но не ** определение **. Следовательно, нет ошибки в компоновщике. –

ответ

2

В вашем примере с двумя отдельными файлами поведение не определено. Вот мой «случай», на основе С11 стандарта:

C11 6.2.2 (4):

For an identifier declared with the storage-class specifier extern ... If no prior declaration is visible, or if the prior declaration specifies no linkage, then the identifier has external linkage.

В test.c, myfun имеет внешнюю связь, поскольку она объявлена ​​с extern и никакого предварительного заявления не является видимый.

С11 6.2.2 (5):

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern.

В mylib.c, myfun объявляется без каких-либо хранения класса спецификатора, поэтому он, как если бы она была объявлена ​​с extern, следовательно, он имеет внешнюю связь в этом перевод. .

C11 6.9.1 (7) [определение функций]:

If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit

Поэтому определение myfun в mylib.c также является декларация myfun (в случае, если какое-либо сомнение).

C11 6.2.2 (2):

In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function.

Поэтому два myfun ы обозначают ту же самую функцию.

C11 6.2.7 (2):

All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined.

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

Обратите внимание, что 6.2.7 (2) не является ограничение, поэтому Clang не обязан выдать диагностическое, когда он нарушается. Однако в одном случае файла, есть фактическое нарушение ограничений, как происходит следующее под заголовком «Ограничения»:

C11 6,7 (4):

All declarations in the same scope that refer to the same object or function shall specify compatible types.

Так Clang должна выдать диагностика в этом случае.

3

Это только не определено поведение. C11 ничего не говорит о линкерах или о том, как они объединяют несколько единиц перевода. На практике это не проблема, так как будет файл заголовка с объявлением функции для myfun(), в который включены оба этих файла C.

+0

Значит ли ваше «неопределенное поведение» что-нибудь может случиться, как стандарты C11? Или вы имеете в виду, что линкеры просто выходят за рамки стандартов C11? –

+0

Я имею в виду, что первая и последняя также верны. В основном, линкеры (по крайней мере, используемые мной Unix и GNU-линкеры) не выполняют никаких проверок типов. Так что диагностика непростая. Но в вашем конкретном примере он также будет работать так же, как ожидалось, во всех системах, поскольку «char *» и «void *» должны иметь одинаковое представление. –