реклама на сайте
подробности

 
 
> Использование extern в нескольких файлах
kolobochishe
сообщение Jan 24 2012, 06:18
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 240
Регистрация: 14-04-10
Из: Россия, г.Челябинск
Пользователь №: 56 634



Почему компилятор (IAR) не выдает ошибки в случае если переменная определена как глобальная в 2-х файлах с одним и тем же именем, а в третьем объявлена как extern? На какую из 2-х глобальных переменных в итоге она (объявленная как extern) будет ссылаться?
Go to the top of the page
 
+Quote Post
2 страниц V   1 2 >  
Start new topic
Ответов (1 - 23)
Палыч
сообщение Jan 24 2012, 06:34
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(kolobochishe @ Jan 24 2012, 10:18) *
...определена как глобальная в 2-х файлах с одним и тем же именем...?

Вероятно, в одном из файлов переменная, всё же, не глобальная. Иначе линкер выдавал бы ошибку.
Go to the top of the page
 
+Quote Post
XVR
сообщение Jan 24 2012, 07:25
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Если программа на С (не С++ !) и переменная в обоих файлах не инициализированна, то она может быть объявленна хоть в 10 файлах. Фича такая в С. Линкер сделает одну переменную на все файлы
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Jan 24 2012, 07:33
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (XVR @ Jan 24 2012, 09:25) *
Если программа на С (не С++ !) и переменная в обоих файлах не инициализированна, то она может быть объявленна хоть в 10 файлах. Фича такая в С. Линкер сделает одну переменную на все файлы
Объявлена (с extern) она может быть сколько угодно раз, а определена (без extern и без static) может быть только в одном, что в С, что в С++.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
kolobochishe
сообщение Jan 24 2012, 07:57
Сообщение #5


Местный
***

Группа: Участник
Сообщений: 240
Регистрация: 14-04-10
Из: Россия, г.Челябинск
Пользователь №: 56 634



да. На простом Си. Не инициализирована.
Забавно. Т.е. даже не использую extern эта переменная будет одна на все модули? как-то опасно. Или единожды где-то указать extern все таки надо?

Ну ситуация в общем такая. Объявлена глобально переменная в разных модулях с одним и тем же именем. Понятно, что это 2 разные глобальные переменные в разных модулях. Только имя одно. Но в третьем я ее записал как extern. Вот на какую из них ссылается extern?

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

Сейчас попробовал объявить глобальные переменные с одинаковыми именами в 2-х модулях и линкер тоже сообщает об ошибках. Разве не должно быть, чтобы переменные хоть и с одним именем, но в итоге должны быть разные?

наверно я чего то недопонимаю. static позволяет сделать глобальную переменную невидимой для других модулей? т.е. мне надо перед глобальными переменными ставить static, чтобы линкер не ругался?
Go to the top of the page
 
+Quote Post
XVR
сообщение Jan 24 2012, 08:01
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(Сергей Борщ @ Jan 24 2012, 11:33) *
Объявлена (с extern) она может быть сколько угодно раз, а определена (без extern и без static) может быть только в одном, что в С, что в С++.
Нет, в С может много раз (хотя лучше так не делать) -

test1.c
Код
int c;

int ret_c1(void)
{
return c;
}


test2.c
Код
int c;

int ret_c2(void)
{
return c;
}


test3.c
Код
#include <stdio.h>

extern int c;

int ret_c1(void);
int ret_c2(void);

int main()
{
c=10;
printf("c1=%d\n",ret_c1());
printf("c2=%d\n",ret_c2());
return 0;
}


Цитата
> gcc test1.c test2.c test3.c -o testx
> ./testx
c1=10
c2=10


А вот если переменные будут инициализированны, тогда ой!

Код
int c=1; // В test1.c и test2.c


Цитата
> gcc test1.c test2.c test3.c -o testx
/tmp/ccIqdWVQ.o:(.data+0x0): multiple definition of `c'
/tmp/ccO4jRpV.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status


Цитата
Объявлена глобально переменная в разных модулях с одним и тем же именем. Понятно, что это 2 разные глобальные переменные в разных модулях.
Нет, линкер сольет их в одну

Цитата
> gcc test1.c -c
> objdump -t test1.o
test1.o: file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 test111.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 000000000000000c ret_c1
0000000000000004 O *COM* 0000000000000004 c
Обратите внимание на '*COM*' (и тип O) у переменной 'c' - это COMMON секция. Линкер выберет одну (произвольно) такую секцию из всех входных файлов и замкнет все ссылки на нее
Go to the top of the page
 
+Quote Post
kolobochishe
сообщение Jan 24 2012, 08:11
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 240
Регистрация: 14-04-10
Из: Россия, г.Челябинск
Пользователь №: 56 634



хм... а ИАР мне так не разрешает. Объявления без инициализации, присвоение внутри функций. И все равно ругается. Может gcc немного по другому делает?
Go to the top of the page
 
+Quote Post
demitar
сообщение Jan 24 2012, 08:12
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 17
Регистрация: 3-09-09
Пользователь №: 52 170



хм, заинтриговали, у меня IAR'овский линкер на такое пишет:
Error[e27]: Entry "c" in module 1 ( D:\Work\avrtest\Debug\Obj\1.r79 ) redefined in module 2 ( D:\Work\avrtest\Debug\Obj\2.r79 )

кто из них не торт?
Go to the top of the page
 
+Quote Post
kolobochishe
сообщение Jan 24 2012, 08:20
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 240
Регистрация: 14-04-10
Из: Россия, г.Челябинск
Пользователь №: 56 634



а так у меня

Error[Li006]: duplicate definitions for "c"; in "C:\Users\Ñàøà\Desktop\Íîâàÿ ïàïêà (4)\Debug\Obj\test1.o", and "C:\Users\Ñàøà\Desktop\Íîâàÿ ïàïêà (4)\Debug\Obj\test2.o"
Go to the top of the page
 
+Quote Post
demitar
сообщение Jan 24 2012, 08:25
Сообщение #10


Участник
*

Группа: Участник
Сообщений: 17
Регистрация: 3-09-09
Пользователь №: 52 170



у меня старый иар, поэтому сообщения отличаются, но суть одна и та же.
Если Вам нужно какую-то конкретную переменную сделать видимой из других модулей, оставьте ее как есть, а все остальные с тем же именем объявите как static
Go to the top of the page
 
+Quote Post
kolobochishe
сообщение Jan 24 2012, 08:30
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 240
Регистрация: 14-04-10
Из: Россия, г.Челябинск
Пользователь №: 56 634



Понятно sm.gif Спасибо за разъяснения. Почему то всегда считал, что глобальные переменные можно называть одинаково в разных модулях и без static. Но, вполне логично, тогда бы и возник вопрос куда ссылается extern если много одинаково именованых глобальных переменных. А так все четко. Не называй одинаково и все ОК sm.gif
Go to the top of the page
 
+Quote Post
XVR
сообщение Jan 24 2012, 08:41
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(kolobochishe @ Jan 24 2012, 12:30) *
Почему то всегда считал, что глобальные переменные можно называть одинаково в разных модулях и без static.
Как видим иногда можно, и поэтому лучше всего никогда этого не делать - черевато трудно уловимыми ошибками (например вы могли их назвать одинаково случайно, а потом будете долго искать почему у вас переменная ни с того ни с сего поменялась в процессе работы)

И если компилятор это поддерживает (как gcc), то лучше это ему запретить (если есть такая возможность)
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jan 24 2012, 08:43
Сообщение #13


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(XVR @ Jan 24 2012, 11:01) *
Нет, в С может много раз (хотя лучше так не делать)...

Нет, это "чудесное" свойство компилятора, который вы используете, но никак не языка C.
Go to the top of the page
 
+Quote Post
XVR
сообщение Jan 24 2012, 10:52
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(ViKo @ Jan 24 2012, 12:43) *
Нет, это "чудесное" свойство компилятора, который вы используете, но никак не языка C.
Я использовал gcc (один из самых распростаненных компиляторов). Но вынужден вас разочаровать - это свойство именно языка С.

Стандарт С (99), глава 6.6.6: Linkages of identifiers
Цитата
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. Within one translation unit, each declaration of an identifier with internal
linkage denotes the same object or function. Each declaration of an identifier with no
linkage denotes a unique entity.

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
. If
the declaration of an identifier for an object has file scope and no storage-class specifier,
its linkage is external.
Так что для С слово extern и его отсуствие - одно и тоже sad.gif

Go to the top of the page
 
+Quote Post
ReAl
сообщение Jan 24 2012, 11:41
Сообщение #15


Нечётный пользователь.
******

Группа: Свой
Сообщений: 2 033
Регистрация: 26-05-05
Из: Бровари, Україна
Пользователь №: 5 417



Цитата(XVR @ Jan 24 2012, 10:41) *
Как видим иногда можно, и поэтому лучше всего никогда этого не делать
в том числе, по вкусу:
максимально об-static-овать все переменные, которые не нужны за пределами модуля
не лениться давать префиксы с именем модуля для глобальных
просто поменьше глобальных (C++ static-переменные класса с доступом через inline-методы рулят)

Цитата(XVR @ Jan 24 2012, 12:52) *
это свойство именно языка С.
Стандарт С (99), глава 6.6.6: Linkages of identifiers
Так что для С слово extern и его отсуствие - одно и тоже sad.gif
Вот...
Позволю себе немного дополнить. Правда, у меня другая нумерация, 6.2.2 (ISO/IEC 9899:1999 (E), слова draft нет, но нет и титульных страниц, надо бы обменяться файлами sm.gif )
Прикрепленный файл  iso9899_c99.pdf ( 1.58 мегабайт ) Кол-во скачиваний: 175


Выделенное в цитатах из стандарта у XVR звучит как то, что объявления переменной в
Код
int ii;
int ii;
int foo() { return ++i; }
int ii=2;

описывают один объект с именем ii (и gcc так и считает).
Т.е. тут даже инициализация есть, но одна. Это полетит в .data, и если в другом файле тоже будет ii, то это будет конфликт (инициализация уже выступает не просто как объявление, но и как определение объекта). Похоже на работу с функцией — прототип можно указать в куче мест, но тело — в одном.
А вот если убрать инициализацию, то оно идёт не в .data, а в .comm с именем переменной. Чтобы в .bss, нужен ключ -fno-common и тогда линкер выругается.

Мне тут не нравится только то, что проверка размеров не производится, если в одном файле int c; а в другом long c;, то линкер даже не заметит. Не его проблема. Очень напоминает COMMON-блоки фортрана :-)
А так — «фича». Несколько непривычно, но вон после ассемблера и integer promotion непривычно.
На проблемы с этим никогда не нарывался, возможно, в силу простой внимательности и простым правилам, которіе я напсиал в начале. В итоге даже ключик нигде не стоит и пришлось полезть глянуть, как он точно звучит (помню, что есть, забыл какой).


--------------------
Ну, я пошёл… Если что – звоните…
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jan 24 2012, 11:50
Сообщение #16


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(XVR @ Jan 24 2012, 13:52) *
Стандарт С (99), глава 6.6.6: Linkages of identifiers

Попытался перевести и понять, что там написано.
П. 5 описывает функции, там другие правила.
Единственное, что подходит - Each declaration of an identifier with no linkage denotes a unique entity.
Но разве из этого следует, что эти уникальные объекты останутся уникальными, если они определены в разных файлах одной программы?
Вот добавляем мы квалификатор static к определению переменной, чтобы ограничить ее видимость в пределах файла. Значит, без static она видна везде? Как же разруливать 2 одинаковых переменных?
Go to the top of the page
 
+Quote Post
Палыч
сообщение Jan 24 2012, 12:00
Сообщение #17


Гуру
******

Группа: Свой
Сообщений: 2 399
Регистрация: 10-05-06
Из: г. Новочеркасск
Пользователь №: 16 954



Цитата(ViKo @ Jan 24 2012, 15:50) *
Но разве из этого следует, что эти уникальные объекты останутся уникальными, если они определены в разных файлах одной программы?

Где-то там, в п.6.2.2 стандарта есть фраза, что если не указано иное, то "по-умолчанию" - extern. T.е указан extern явно, или не указано ничего, то это тоже extern, а значит - один и тот же объект.
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Jan 24 2012, 12:02
Сообщение #18


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



По умолчанию в Си переменные имеют внешнее связывание, в Си++ -- внутреннее.
Go to the top of the page
 
+Quote Post
XVR
сообщение Jan 24 2012, 14:18
Сообщение #19


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(ViKo @ Jan 24 2012, 15:50) *
Единственное, что подходит - Each declaration of an identifier with no linkage denotes a unique entity.
Но разве из этого следует, что эти уникальные объекты останутся уникальными, если они определены в разных файлах одной программы?
Они будут уникальными по именам для всей программы, в скольких бы модулях их не описали.
Вот -
Цитата
In the set of translation units and libraries that constitutes an entire program,

Что можно перевести как 'в наборе единиц компиляции и библиотек, который является всей программой'

Цитата
П. 5 описывает функции, там другие правила.
Во 2м предложении оттуда речь идет уже о переменных (я немного не то предложение выделил)

Цитата
Вот добавляем мы квалификатор static к определению переменной, чтобы ограничить ее видимость в пределах файла. Значит, без static она видна везде?
Да
Цитата
Как же разруливать 2 одинаковых переменных?
Их объединит линкер (если они обе не инициализированные), или получите слом на линковке
Go to the top of the page
 
+Quote Post
dxp
сообщение Jan 24 2012, 15:49
Сообщение #20


Adept
******

Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343



QUOTE (andrew_b @ Jan 24 2012, 19:02) *
По умолчанию в Си переменные имеют внешнее связывание, в Си++ -- внутреннее.

Константы, вы хотели сказать?


--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Jan 25 2012, 04:36
Сообщение #21


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



Нет, переменные. И функции тоже. Про константы утверждать не буду, но скорее всего, тоже.
Go to the top of the page
 
+Quote Post
am1808
сообщение Feb 3 2012, 10:11
Сообщение #22


Частый гость
**

Группа: Участник
Сообщений: 125
Регистрация: 29-05-11
Из: Nizhny Novgorod, Russian Federation
Пользователь №: 65 337



Цитата(andrew_b @ Jan 25 2012, 08:36) *
Нет, переменные. И функции тоже. Про константы утверждать не буду, но скорее всего, тоже.


ключевое слово const ( а также typedef ) дает имени внутреннее связывание.

при использовании имен без headers, например, в файле 1.с

const int a = 5;
то a имеет внутреннее связывание.

для того, чтобы а имела внешнее связывание, нужно

в файле 1.с

объявить a как extern;
Код
extern const int a; //  переменная a теперь будет иметь внешнее связывание


и определить переменную:
Код
const int a = 777; // определяем a


теперь, для использования a в других единицах трансляции нужно просто объявить переменную

файл 2.с:

Код
extern const int a; // далее, можем использовать a



тоже самое достигается путем headera

теперь,
если же мы определяем в header что-то типа:

const int b = 10;

и включаем это header в различные единицы трансляции,
то в каждой единице трансляции создается своя локальная копия b

Сообщение отредактировал am1808 - Feb 3 2012, 10:13
Go to the top of the page
 
+Quote Post
Marto
сообщение Feb 14 2012, 05:42
Сообщение #23


Частый гость
**

Группа: Свой
Сообщений: 103
Регистрация: 17-05-09
Из: Ижевск
Пользователь №: 49 190



XVR,
а мне помнится, что во исходном файле глобальная переменная объявляется как обычном, а во всех остальных со спецификатором extern. где-то валялась книжка по стандарту C99.
Да и gcc для авр не позволял делать так, как вы описали.


--------------------
Шизоидный холерик
Go to the top of the page
 
+Quote Post
XVR
сообщение Feb 14 2012, 08:18
Сообщение #24


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(Marto @ Feb 14 2012, 09:42) *
а мне помнится, что во исходном файле глобальная переменная объявляется как обычном, а во всех остальных со спецификатором extern.
И это правильно laughing.gif
Цитата
где-то валялась книжка по стандарту C99.
Но стандарт не запрещает написать переменную многократно и без extern (IMHO из соображений совместимости - во времена K&R это была обычная практика)

Цитата
Да и gcc для авр не позволял делать так, как вы описали.
А x86 позволил
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 11th August 2025 - 22:03
Рейтинг@Mail.ru


Страница сгенерированна за 0.02551 секунд с 7
ELECTRONIX ©2004-2016