2016-10-06 15 views
1

Я обнаружил, что могу достичь желаемых результатов без использования extern (хотя я согласен с тем, что он дает читателю какой-то намек на переменную). В некоторых случаях использование extern давало нежелательные результаты.Является ли ключевое слово extern в C избыточным?

xyz.h

int i; 

file1.c

#include "xyz.h" 
.... 
i=10; 
.... 

file2.c

#include "xyz.h" 
main() 
{ 
printf("i=%d\n",i); 
} 

Конечно, это был большой проект, сломал его вниз для простого понимания. С ключевым словом extern я не смог получить желаемые результаты. На самом деле, я получил ошибку компоновщика для переменной i с «внешним» подходом.

код с "экстерном" подход,

file1.c

int i; 
main() 
{ 
i=10; 
} 

file2.c

extern int i; 
foo() 
{ 
printf("i=%d\n",i); 
} 

Это дало ошибку компоновщика. Я просто хотел знать, почему это сработало в первом случае, а также практический случай, когда мы не можем сделать это, не используя ключевое слово «extern». Благодарю.

+0

Во втором случае у вас есть две функции, называемые 'main'. –

+0

http://port70.net/~nsz/c/c11/n1570.html#6.2.2 – Olaf

+0

Извинения. file2.c does not have main() – Harsha

ответ

1

Существует, по крайней мере, 2 случая, где extern имеет смысл, а не «лишний»:

  1. Для объектов (не функции) в области видимости файла, он объявляет объект с внешним связыванием без предоставления предварительного определения; предварительные определения превращаются в полные определения в конце единицы перевода и не имеют одинакового идентификатора, определенного с внешней связью в нескольких единицах перевода.

  2. В области объема (в функции) extern позволяет объявлять и получать доступ к объекту или функции с помощью внешней связи, не принося идентификатор в область видимости файла (поэтому он не отображается за пределами блока с объявлением). Это полезно, если имя может конфликтовать с чем-то другим в области файлов. Например:

    static int a; 
    int foo(void) 
    { 
        return a; 
    } 
    int bar(void) 
    { 
        extern int a; 
        return a; 
    } 
    

    без extern ключевого слова в bar, int a; даст локальную переменную (автоматическое хранение).

+0

Итак, foo обращается к статическому глобальному 'a', а bar ссылается на 'a', который определен в каком-то другом файле? – Harsha

+0

@ Харша: Точно. –

+0

Очень изобретательный. Что-то вроде пространства имен в C++, если я прав – Harsha

6

Формально ваша первая программа недействительна. Определение переменной в файле заголовка, а затем включение этого файла заголовка в несколько единиц перевода, в конечном итоге приведет к нескольким определениям одного и того же объекта с внешней связью. Это нарушение ограничений на C.

6.9 Внешние определения

Внешнее определение является внешним свидетельством того, что является также определение функции (иное, чем определение инлайн) или объект . Если идентификатор, объявленный с внешней связью, используется в выражении (кроме как как часть операнда оператора sizeof , результатом которого является целочисленная константа), где-то во всей программе должно быть точно одно внешнее определение для идентификатора ; в противном случае должно быть не более одного.

Определения i в первом примере является предварительного определения (как это было упомянуто в комментариях), но он превращается в обычное полноценный внешнего определение из i в конце каждого которая включает файл заголовка. Таким образом, «tentativeness» этого определения ничего не меняет с точки зрения «всей программы». На самом деле это не так важно (в сторону, для небольшого замечания ниже).

Что делает ваш первый пример для компиляции без ошибок, является популярным расширением компилятора, которое даже упоминается как таковое в стандарте языка.

J.5 Общих расширений

J.5.11 Несколько внешних определения

Там может быть более чем один внешнего определения идентификатора объекта, с или без явного использования ключевого слова экстерна ; если определения не совпадают, или более одного инициализируется, поведение не определено (6.9.2).

(Вполне возможно, что первоначально привело к тому расширения компилятора в C есть несколько конкретных воплощений особенности поддержки предварительного определения, но на абстрактном уровне языка предварительные определения не имеют ничего общего с этим.)

Ваш вторая программа действительна в отношении i (BTW, неявный int больше не поддерживается в C). Я не вижу, как вы можете получить от него какие-либо ошибки компоновщика.

+1

'int i;' t-область-файл - это просто _tentative definition_, а не фактическое определение. – Olaf

+0

@ Олаф: Тем не менее предварительное определение становится актуальным в обоих ТУ, не так ли? –

+1

@ Олаф: Да, но это совсем не то, что нужно. Предварительное определение становится регулярным определением в конце единицы перевода. И наличие того же внешнего объекта, определенного в двух разных единицах перевода, является таким же незаконным в C, как и на C++. – AnT