использование внешнего ключевого слова

У меня есть три программы, в которых я использую ключевое слово extern. Я не могу понять результат. Ниже приведены три примера:

Пример 1: я ожидал, что приведенный ниже код выдаст ошибку компиляции, связанную с множественным объявлением k. Но работает нормально?

int k; //works fine
extern int k = 10;

void main()
{
    cout<<k<<endl;
    getchar();
}

Пример 2: когда я пытаюсь инициализировать "k" в приведенном выше примере, компилятор выдает ошибку. Почему?

int k = 20; //error
extern int k = 10;

void main()
{
    cout<<k<<endl;
    getchar();
}

Пример 3: В этом примере я изменил порядок определений, упомянутых в примере 1. Когда я компилирую этот код, я получаю ошибки. Почему?

extern int k = 10;
int k;   //error

void main()
{
    cout<<k<<endl;
    getchar();
}

person anu    schedule 28.07.2011    source источник
comment
+1 например 3. Кроме того, 1-й пример не является хорошей практикой.   -  person iammilind    schedule 28.07.2011
comment
С или С++? int k; в области файла означает разные вещи на этих двух языках.   -  person CB Bailey    schedule 28.07.2011


Ответы (9)


Пример 2. Вы пытаетесь дважды инициализировать глобальную переменную с двумя разными значениями. Это ошибка.

Пример 3. Сначала вы объявляете переменную extern, а затем определяете переменную с тем же именем в той же единице компиляции. Это невозможно.

person Didier Trosset    schedule 28.07.2011
comment
Рассуждение, которое вы привели для примера 3, неприменимо также и для примера 1? Я должен получить ошибку даже в примере 1. - person anu; 28.07.2011
comment
@anu: В примере 1 вы объявляете их другим в обратном порядке: в то время, когда компилятор создает внешнюю переменную, я не знаю, что фактическая переменная будет в том же модуле компилятора. - person Didier Trosset; 28.07.2011

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

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

Правила:

  1. Используйте extern для ссылки, а не для определения переменной.

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

  3. Поместите все ваши внешние файлы в файл заголовка include (.h) и сделайте #include в ваших файлах cpp, этот способ более удобен и помогает навести порядок в исходном коде.


Посмотрите этот пример, где я использую все 3 правила:

Мой файл module1.cpp:

unsigned short int AGLOBAL = 10; // definer and initializer

void MyFunc(void)
{
  AGLOBAL+=1; // no need to include anything here cause is defined above
  // more .....    
}

Мой файл заголовка globals.h:

// this is to include only once
#ifndef MYH
#define MYH
extern unsigned short int AGLOBAL; // no value in here!

#endif

Другой файл module2.cpp:

#include globals.h

char SomeOtherFunc(void)
{
  AGLOBAL+=10; // ok cause its declared by globals.h
  // do more....
}
person Community    schedule 28.07.2011

Использование ключевого слова extern должно сообщить компилятору, что:

Переменная определяется извне.

Первая программа должна выдать ошибку. Какой компилятор вы используете? Кстати, void main() не является стандартным. Ни в С, ни в С++.

person Jagannath    schedule 28.07.2011
comment
Я использую Visual Studio 2008. Ну, это просто обучение, поэтому я проигнорировал возвращаемый тип main. Кстати спасибо за информацию. - person anu; 28.07.2011
comment
@ану, хорошо. Если это для обучения, то гораздо лучше использовать более совместимый со стандартами компилятор, например. VS2010 Express Edition (бесплатно), последние версии g++. - person Jagannath; 28.07.2011

Ваш компилятор неряшлив. Компилируя этот тривиальный вариант (включая заголовок и объявление using), я получаю:

$ cat xxx.cpp
#include <iostream>
using namespace std;

int k; //works fine
extern int k = 10;

void main()
{
cout<<k<<endl;
getchar();
}
$ g++ -c  xxx.cpp
xxx.cpp:5:12: warning: ‘k’ initialized and declared ‘extern’ [enabled by default]
xxx.cpp:5:12: error: redefinition of ‘int k’
xxx.cpp:4:5: error: ‘int k’ previously declared here
xxx.cpp:7:11: error: ‘::main’ must return ‘int’
$ g++ --version
g++ (GCC) 4.6.0
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$

Какой компилятор вы используете?

person Jonathan Leffler    schedule 28.07.2011
comment
+1 аналогичный код выдаст ошибку для обычной программы c на gcc (GCC) 4.6.2 20111027 (Red Hat 4.6.2-1). - person CppLearner; 12.02.2012

int k;
extern int k = 10; 

Это нормально в C, где вы можете иметь «предварительное определение» в дополнение к реальному. В С++ это запрещено. Там попытка объявить одну и ту же переменную дважды.

Мой компилятор C также предупреждает меня, что одновременное выполнение extern и инициализации необычно.

int k = 20; //error
extern int k = 10;    

Это попытка дать k два разных значения, что, конечно, не работает.

extern int k = 10;
int k;   //error 

Похоже, что это эквивалентно случаю 1. Это не разрешено в C++, но допустимо в C99.

person Bo Persson    schedule 28.07.2011

Случай 1: выдает ошибку переопределения в c++(gcc-4.3.4)

Случай 2: выдает ошибку переопределения в c++(gcc-4.3.4)

Случай 3: выдает ошибку переопределения в c++(gcc-4.3.4)

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

Ссылка:
Стандарт C++: 3.1 Объявления и определения

3 [Example: all but one of the following are definitions:

int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
......

В приведенном выше фрагменте a и c оба являются определениями.

Если ваш компилятор не выдает ошибку во всех трех случаях, значит, он неисправен в том, что касается C++.

person Alok Save    schedule 28.07.2011

Я понимаю примеры 2 и 3. Но то, что пример 1 был скомпилирован, это так странно. gcc никогда бы не скомпилировал такой код.

person mikithskegg    schedule 11.02.2012

Для третьего, вы действительно хотите это

extern int k = 10;
extern int k;   //okay: this is just a declaration. 
// extern int k = 4;  re-define is no good.

void main()
{
cout<<k<<endl;
getchar();
}

Вы можете определить переменную только один раз. Однако вы можете объявить столько раз, сколько захотите.


Чтобы указать немного дальше, int i; является и объявлением, и определением. Часто инициация времени понимается как «определение». Для автоматической переменной и объявление, и определение выполняются в одном операторе.

Итак, когда мы определяем int k;, была выделена память, ссылка на имя которой «k». Следовательно, компоновщик будет жаловаться, когда вы попытаетесь переопределить его.

int k;
extern int k = 3;  // already defined in the previous statement

Следовательно, это также ошибка компиляции

extern int k = 3;
int k;    // trying to redefine again - bad

Это, вероятно, применяется только в C++. Я не знаком с C, поэтому не могу говорить о C. В C++ даже мое решение будет жаловаться, но не выдаст ошибку.

Пожалуйста, оцените меня и исправьте мои ошибки. я тоже учусь.

person CppLearner    schedule 11.02.2012
comment
@CharlesBailey спасибо. Я пропустил int во втором утверждении. Это точно работает :D - person CppLearner; 12.02.2012

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

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

Любое объявление, в котором переменной присвоено значение, считается сильным. Кроме того, если присваивания нет, то ключевое слово extern заставит переменную ссылаться на другую переменную, если она существует (фактически делая самую слабую из слабых).

Случай 1: у вас есть слабое объявление, за которым следует сильное. Это здорово. Случай 2: у вас есть два сильных объявления. Ошибка! Случай 3: у вас есть сильное заявление, за которым следует слабое. Я на самом деле не уверен, почему этот терпит неудачу. Если бы я предположил, я бы сказал, что второе объявление рассматривается компоновщиком как сильное.

person Abhay Buch    schedule 29.07.2011