Подтвердить что ты не робот

Странное поведение статической глобальной переменной

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

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);
}

Extern.h:

namespace Header {
    static int i;
};

Выход, который я получаю:

i : 5
i : 0

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

4b9b3361

Ответ 1

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

Ответ 2

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

Откуда это 0?

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

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

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

Ответ 3

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

static int i;

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

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

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

namespace Header {
    static int i;
};

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

namespace {
    int i;
};

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

Ответ 4

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

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

Заголовок:

namespace Header {
    extern int i;
}

Cpp:

namespace Header {
    int i = 0;
}

Ответ 5

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

Итак, 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;

Ответ 6

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

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

Ответ 7

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

Ответ 8

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