|
Использование extern в нескольких файлах |
|
|
|
Jan 24 2012, 07:33
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Jan 24 2012, 08:01
|
Гуру
     
Группа: Свой
Сообщений: 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 секция. Линкер выберет одну (произвольно) такую секцию из всех входных файлов и замкнет все ссылки на нее
|
|
|
|
|
Jan 24 2012, 08:12
|
Участник

Группа: Участник
Сообщений: 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 )
кто из них не торт?
|
|
|
|
|
Jan 24 2012, 08:25
|
Участник

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

|
у меня старый иар, поэтому сообщения отличаются, но суть одна и та же. Если Вам нужно какую-то конкретную переменную сделать видимой из других модулей, оставьте ее как есть, а все остальные с тем же именем объявите как static
|
|
|
|
|
Jan 24 2012, 08:41
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(kolobochishe @ Jan 24 2012, 12:30)  Почему то всегда считал, что глобальные переменные можно называть одинаково в разных модулях и без static. Как видим иногда можно, и поэтому лучше всего никогда этого не делать - черевато трудно уловимыми ошибками (например вы могли их назвать одинаково случайно, а потом будете долго искать почему у вас переменная ни с того ни с сего поменялась в процессе работы) И если компилятор это поддерживает (как gcc), то лучше это ему запретить (если есть такая возможность)
|
|
|
|
|
Jan 24 2012, 10:52
|
Гуру
     
Группа: Свой
Сообщений: 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 и его отсуствие - одно и тоже
|
|
|
|
|
Jan 24 2012, 11:41
|

Нечётный пользователь.
     
Группа: Свой
Сообщений: 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 и его отсуствие - одно и тоже  Вот... Позволю себе немного дополнить. Правда, у меня другая нумерация, 6.2.2 (ISO/IEC 9899:1999 (E), слова draft нет, но нет и титульных страниц, надо бы обменяться файлами  )
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 непривычно. На проблемы с этим никогда не нарывался, возможно, в силу простой внимательности и простым правилам, которіе я напсиал в начале. В итоге даже ключик нигде не стоит и пришлось полезть глянуть, как он точно звучит (помню, что есть, забыл какой).
--------------------
Ну, я пошёл… Если что – звоните…
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|