При каких обстоятельствах можно использовать внешнюю переменную в определении? - PullRequest
2 голосов
/ 09 ноября 2011

Мне очень очень жаль. Я не знал, что мое неполное вложение кода создаст такой беспорядок. Я очень рад видеть столько искренней помощи.

Этот код скомпилируется:

int myadd(int, int);
static int main_stat = 5;

int main()
{
    int i, j;
    main_stat = 13;
    j = myadd(-1,7);
    i = main_stat;

    cout << j << i;     //  3  and 13
    return 0;

}

myadd.cpp

extern int main_stat = -3;
int myadd(int x,int y)
{
    int t = main_stat;
    t = x + y;
    y = t +main_stat;
    return y;    // will return 3
}

См. Я определил и внешнюю ссылку main_stat. Почему это законно? Я думал, что вы могли только связать, а не определить.

Распределена ли память в кадре стека вызова функции myadd? Глобальная статика размещается в куче, я полагаю, верно?


EDIT

Извините, но я думаю, что на этот раз я сужу свои вопросы:

Из C ++ Primer 4ed

Объявление extern может содержать инициализатор (при объединении становится определением), только если оно появляется вне функции.

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

Q1. Какую копию main_stat использует myadd (int, int) при вызове? Та же копия, что и у основной, но с другим значением (которое я могу проверить)? Или каждая функция имеет свою собственную статическую глобальную копию?

Q2. Выделена ли память в куче для этих глобальных статических переменных? Я знаю, что многое зависит от реализации, но разве куча не используется для статических переменных?

Q3. Я знаю, что следующие два действительны

extern int x;    // means int x is defined elsewhere
extern int x = 3;  // declared and defined 

Зачем нам второй, если мы можем просто объявить статическую глобальную переменную в пространстве имен myadd? Как это проясняет ситуацию , как сказал Ашеплер?

Ответы [ 6 ]

9 голосов
/ 09 ноября 2011

Все объявления переменных с инициализатором также являются определениями; это главное правило. Независимо от extern. Есть даже случаи, когда вам нужно extern для определения: вы можете только создать шаблон с помощью переменной, имеющей внешнюю связь А также const переменные имеют внутреннюю связь по умолчанию, поэтому вам нужно что-то вроде:

extern int const i = 42;

, если вы хотите использовать его для создания экземпляра template<int const*>.

3 голосов
/ 09 ноября 2011

Ниже приводится объявление и определение:

int x;

Добавление extern гласит «сделайте только декларацию, пожалуйста».

Но когда вы предоставляете значение, строка имеет для определения, поэтому переменная получает extern класс хранения, и вы все равно просто определяете его на месте:

extern int x = 3;

Семантика связи такая же, как обычно для extern, а место хранения такое же, как и для обычного определения int x = 3 & mdash; то есть в этом TU в области пространства имен. myadd не имеет отношения к делу.


Трудно "доказать", потому что это случай "нет правил против этого".

Вот лучшая цитата:

[n3290: 3.1/2]: Декларация является определением, если она не декларирует функция без указания тела функции (8.4), это содержит спецификатор extern (7.1.1) или спецификацию связи 25 (7.5) и не содержит ни инициализатора, ни функция-тело , [..]

и некоторая другая соответствующая информация:

[n3290: 3.5/2]: Говорят, что имя имеет связь , когда оно может обозначать тот же объект, ссылку, функцию, тип, шаблон, пространство имен или значение как имя, введенное объявлением в другой области видимости:

  • Когда имя имеет внешнюю связь , на обозначаемую им сущность можно ссылаться по именам из областей других единиц перевода или из других областей той же единицы перевода.
  • Когда имя имеет внутреннюю связь , обозначаемая им сущность может именоваться именами из других областей в той же единице перевода.
  • Когда имя не имеет связи , обозначаемая им сущность не может именоваться именами из других областей.

[n3290: 3.5/12]: Имя функции, объявленной в области видимости блока, и имя переменной, объявленной в области видимости блока extern Объявление есть связь. Если есть видимое объявление объекта с связь, имеющая то же имя и тип, игнорируя объявленные объекты вне самой внутренней охватывающей области пространства имен, области блока декларация объявляет ту же самую сущность и получает связь предыдущая декларация. Если существует более одного такого совпадающего объекта, программа плохо сформирована. В противном случае, если не найдено ни одного подходящего объекта, сущность блока блока получает внешнюю связь. [..]

2 голосов
/ 09 ноября 2011

Вопрос, видимо, проистекает из некоторого заблуждения.

Некоторые люди считают, что ключевое слово extern всегда превращает определение в неопределяющую декларацию. Это просто неправда.

Ключевое слово extern просто дает объявленной сущности внешнюю связь . Может применяться к декларациям . Его можно применять к определениям (и помните, что определения также являются объявлениями ).

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

Путаница обычно вызвана тем фактом, что при применении extern к определению, подобному

int x; // no initializer

это определение внезапно превращается в неопределяющую декларацию. Это правда, но это не более, чем одноразовая изюминка ключевого слова extern, которую нужно запомнить. Если вы берете определение, как

int x = 42;

и затем применение к нему ключевого слова extern все равно сохранит его как определение, т. Е. В этом случае не возникнет причуд.

2 голосов
/ 09 ноября 2011
extern int main_stat=-3;  

объявляет и определяет main_stat, а:

extern int main_stat;      

просто объявляет переменную main_stat.

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

Ключевое слово extern указывает на внешнюю связь.Без него main_stat был бы статичным и имел бы внутреннюю связь, и вы не могли бы использовать main_stat из другого модуля перевода.

Распределена ли память в кадре стека myadd?

Нет, определенно нет на стековом кадре add.
Место, в котором выделяется память, определяется реализацией, но у вас есть уверенность, что объект будет работать в течение всей программы.

1 голос
/ 09 ноября 2011

Во-первых, согласно вашему комментарию, файл, содержащий основную функцию, имеет определение static int main_stat = 10;.Вы должны знать, что это not та же самая переменная, которую вы определили в файле, содержащем myadd, потому что как статическая переменная ее область действия ограничена этим файлом.Действительно, благодаря этой статической переменной с тем же именем main не может получить доступ к переменной, которую вы определили в этом файле.

Но это не означает, что любая переменная была создана в стеке.Оба являются отдельными глобальными переменными, просто переменная main_stat в файле, содержащем main (для краткости я назову этот главный файл и этот файл myadd), недоступна в любом другом файле, в то время как переменнаяmain_stat Вы определили здесь, можно получить доступ из любого файла, который содержит объявление extern main_stat; (примечание: без инициализатора!).Однако основной файл не может содержать это объявление, потому что оно будет конфликтовать со статической переменной с тем же именем.

Обратите внимание, что предоставление инициализатора делает ваше объявление переменной определением, то есть оно совпадает сесли вы пропустили extern (заметьте, однако, что если переменная объявлена ​​константой, extern не может быть опущен, поскольку константы по умолчанию являются статическими).Единственными глобальными внешними объявлениями, которые также не являются определениями, являются с extern, но без инициализатора.

0 голосов
/ 09 ноября 2011

Все остальные довольно хорошо это рассмотрели, но только для того, чтобы показать варианты в одном месте:

int x;                    // #1

- это декларация и определение. Начальное значение x равно нулю.

int x = 3;                // #2

является декларацией и определением.

const int cx;             // #3

недопустимо в C ++.

const int cx = 3;         // #4

является объявлением и определением, но cx имеет внутреннюю связь, если это его первое объявление в единице перевода.

extern int x;             // #5

является декларацией, но НЕ определением. Где-то еще в программе должно быть определение x.

extern int x = 3;         // #6

является декларацией и определением. extern не нужен, но проясняет ситуацию.

extern const int cx;      // #7

является декларацией, но НЕ определением. Где-то еще в программе должно быть определение cx.

extern const int cx = 3;  // #8

- это декларация и определение. extern требуется, если предыдущее объявление выше не было замечено.

...