2015-06-25 3 views
28

Я знаю, что эта программа не использует статическую переменную соответствующим образом, но он показывает, как воспроизвести поведение я видел:Странное поведение статической глобальной переменной

main.cpp:

int main(){ 
    MyObject* p = new MyObject(); 
    Header::i = 5; 

    printf("i %i\n", Header::i); 
    p->update(); 

    return 0; 
} 

myobject.cpp:

MyObject::MyObject(){ 
} 

void MyObject::update(){ 
    printf("i %i\n", Header::i); 
} 

Экстер N час:

namespace Header { 
    static int i; 
}; 

Выход я получаю:

i : 5 
i : 0 

Почему я не получаю 5 для обоих выходов? Откуда у этого 0? Не могли бы вы объяснить, как работают статические переменные?

+3

Рассмотрим, как '# include' работает, и что 'static' означает для нелокальных нечленов переменных. – immibis

+2

Вы также должны опубликовать '# include' в каждом файле cpp, чтобы было ясно, что вы включаете 'Extern.h' в два файла cpp, следовательно, создавая две переменные' i'. – chi

+0

Вы использовали [включить охранников] (https://en.wikipedia.org/wiki/Include_guard)? – ST3

ответ

50

Статические переменные имеют внутреннюю связь, которая фактически означает, что они являются локальными для блока компиляции. Поскольку у вас есть статическая переменная, объявленная в заголовке, включенного в 2 исходных файлов, вы в основном имеют 2 различных переменных: одна i местного до MyObject.cpp и другой, разные i, локальные для main.cpp

+0

Это очень интересно! Если бы я хотел использовать его как «реальную» глобальную переменную, что делать? – Arcyno

+18

не использовать 'static'. Используйте 'extern' в заголовке с инициализацией в одном исходном файле. Но глобальные переменные следует избегать, как чума. – bolov

12

У вас есть две переменные я

static int i; 

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

Если вы удалите спецификатор static, тогда компоновщик должен выпустить сообщение о том, что переменная определена дважды.

Тот же эффект может быть достигнут, если поместить переменную в безымянном пространстве имен в C++ 2011. Например, вместо

namespace Header { 
    static int i; 
}; 

вы могли бы написать

namespace { 
    int i; 
}; 

В этом случае Название переменной я также имеет внутреннюю связь. Это справедливо для C++ 2011.

3

Переменная, которая объявлена ​​как статическая, имеет область видимости только в том файле, в котором она объявлена, где в качестве переменной, объявленной без статики, можно получить доступ из других файлов с использованием объявления extern.

4

Вы получаете путаницу с статической переменной уровня класса с статической переменной уровня пространства имен. К обоим доступны квалификация X::y, что добавляет путаницы. Другие объяснили фактическую причину (на уровне компиляции/связи).

13

У вас есть одна статическая переменная на единицу перевода, где вы включаете заголовок, потому что статические переменные имеют внутреннюю привязку.

Откуда это 0?

Вы не инициализировали переменную во втором блоке перевода, а статические переменные инициализируются нулем, откуда и исходит 0.

В стандарте (§3.6.2/2):

Переменные со статической продолжительностью хранения (3.7.1) [...] должна быть нулевой инициализируется (8.5), прежде чем любой другой инициализации принимает место. [...]

5

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

static int i ; // i has internal linkage 
extern int i ; // i has external linkage 
9

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

Что вы можете сделать, это ехЬегп спецификатор хранения:

Заголовок:

namespace Header { 
    extern int i; 
} 

Cpp:

namespace Header { 
    int i = 0; 
} 
7

В дополнение к все ответы. ПОЧЕМУ это случается, уже объяснялось. Однако КАК ее исправить было предложено до сих пор только с использованием статического/внешнего подхода. Это немного похоже на C. Unles вам не нужно использовать заголовок в C-части проекта с C-linkage, вы можете использовать C++.

So IF у вас есть действительно что-то статическое в вашем коде.

Либо объявить переменную в качестве члена класса:

header.h

MyGlobalVariableHoler 
{ 
    public: static int i; 
}; 

main.cpp

// class' statics have has to be initialized, otherwise linker error. 
int MyGlobalVariableHoler::i=0; 

any_code.cpp

#include <header.h> 

MyGlobalVariableHolder::i=4711; 

Или использовать синглтон, чтобы избежать явного инициализацию

header.h

MyGlobalVariableHolder 
{ 
    MyGlobalVariableHolder(){i=0;} 
    public: 
    static MyGlobalVariableHolder & instance() 
    { 
     static MyGlobalVariableHolder inst; 
     return inst; 
    } 
    int i; 
}; 

any_code.cpp

#include <header.h> 
MyGlobalVariableHolder::instance().i=4711; 
+0

Почему это было бы лучше, чем просто: 'extern in i;' в заголовке, а затем реальное объявление внутри main.cpp 'int Header :: i = 0;'? – Arcyno

+0

Вы имеете в виду подходы singleton? Здесь статичность - статическая локальная функция. Он всегда инициализируется перед вызовом instance() в первый раз. Глобальная статика не может быть инициализирована. –

Смежные вопросы