LINUX.ORG.RU

gcc внешнее связывание

 ,


1

4

Пусть программа состоит из 3х файлов:

//file1.c
#include <stdio.h>

int x;

void f1()
{
	printf("in f1 x= %d\n", x);
}
//file2.c
#include <stdio.h>

int x;

void f2()
{
	printf("in f2 x= %d\n", x);
}
//main.c
void f1();
void f2();

int x = 3;

int main()
{
	f1();
	f2();
}

Как я понимаю, в соответсвии со стандартом, переменные, объявленные в начале файла, обладают внутренним связыванием (internal linkage). То есть этот код компилироваться не должен. Чтобы было правильно, нужно ставить перед int x слово extern, а ровно в одном .c файле должно встретиться определение int x. Но gcc данный код спокойно компилирует, даже с -Wall -Wpedantic . Значение x печатается в обоих случаях 3.

Это какая-то особенность gcc? Или я что-то не так понял в определении связывания?

★★★★

Ответ на: комментарий от anonymous

Я знаю. Но по стандарту оно в таком виде вообще не должно компилироваться. Там написано (С11):

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. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

То есть функции обладают внешним, а переменные - внутренним связыванием. Практически, по умолчанию должно быть static, а не external.

hotpil ★★★★
() автор топика
Ответ на: комментарий от hotpil

Ну как минимум, оно добавит читабельности. Потому как при будущем прочтении будет понятно откуда значение изменилось.

Aswed ★★★★★
()
Последнее исправление: Aswed (всего исправлений: 1)
Ответ на: комментарий от mittorn

Была бы для определения. Объявлений функции может быть сколько угодно, то есть прототипов на деле.

hotpil ★★★★
() автор топика
Ответ на: комментарий от hotpil

обэявление не создаёт символа.
Сменишь компилятор на шланг или студию - не заработает вроде. Ещё есть опция, которая запрещает объединение символов, но не помню, как называется

mittorn ★★★★★
()
Ответ на: комментарий от fsb4000

Ну да. На самом деле меня запутало то, что объявления внутри блока имеют internal linkage по умолчанию. А file scope - да, external.

hotpil ★★★★
() автор топика
Ответ на: комментарий от mittorn

обэявление не создаёт символа.

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

hotpil ★★★★
() автор топика
Ответ на: комментарий от mittorn

Сменишь компилятор на шланг или студию - не заработает вроде.

Проверил. Работает и с gcc, и со шлангом. А вот с g++ действительно не работает. Ну и если инициализировать в разных модулях (даже одинаковым значением), то тоже, естественно, не работает.

Сам был удивлён. Всегда считал, что так писать нельзя. Но, видимо, так нельзя только в си++, а в си можно. Потому что проверял со всеми возможными стандартами -std от =c89 до =c11.

aureliano15 ★★
()

Это какая-то особенность gcc?

За это отвечает не компилятор а binutils. Binutils является частью ОС а не компилятора.

Или я что-то не так понял в определении связывания?

1. В классических стандартах и компиляторах по умолчанию все и всегда внешнее.

2. прям сейчас в стандартах перекраивают традиционное поведение поэтому имеет место некоторая путаница.

cvv ★★★★★
()
Ответ на: комментарий от mittorn

Возможно, древний сищный код это использует и оставлено для совместимости

Да, скорее всего из-за легаси, потому что иначе логичнее было бы запретить. И для компилятора проще, и для людей понятнее, когда везде, кроме одного модуля, явно написано extern.

aureliano15 ★★
()
Ответ на: комментарий от cvv

по умолчанию все и всегда внешнее

Да. Но в данном случае речь об объявлении одной и той же внешней переменной в нескольких модулях без явного указания, что она extern. В си++ такое не прокатывает.

прям сейчас в стандартах перекраивают традиционное поведение

Ключевое слово extern и в си, и в си++ существует с незапамятных времён.

aureliano15 ★★
()

Недавно обсуждали. Это популярное расширение:

J.5.11 Multiple external definitions

There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).
xaizek ★★★★★
()

Самое страшное знаете в чём? Что никто не сказал, что ошибка таки появится, если вы проинициализируете в обоих файлах. Домашнее задание для местных C-«экспертов»: чем отличается

int x;
от
int x = 0;

vodz ★★★★★
()
Ответ на: комментарий от xaizek

Недавно обсуждали. Это популярное расширение:

--warn-common

Да, с опцией -Xlinker --warn-common генерятся ворнинги. Хотя, имхо, лучше бы генерились по дефолту и ошибки, а не ворнинги.

aureliano15 ★★
()
Ответ на: комментарий от vodz

никто не сказал, что ошибка таки появится, если вы проинициализируете в обоих файлах

сказал же.

gcc внешнее связывание (комментарий) :

Ну и если инициализировать в разных модулях (даже одинаковым значением), то тоже, естественно, не работает.

aureliano15 ★★
()
Ответ на: комментарий от mittorn

-fno-common вроде

Точно. Оно самое:

collect2: ошибка: выполнение ld завершилось с кодом возврата 1

aureliano15 ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.