При рефакторинге некоторых #defines
я натолкнулся на объявления, похожие на следующие в заголовочном файле C ++:
static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;
Вопрос в том, какая разница, если таковая будет, от статики? Обратите внимание, что многократное включение заголовков невозможно из-за классического #ifndef HEADER
#define HEADER
#endif
трюка (если это имеет значение).
Означает ли статика, что VAL
создается только одна копия , если заголовок включен более чем в один исходный файл?
Ответы:
Это
static
означает, чтоVAL
для каждого исходного файла, в который он включен, будет создана одна копия . Но это также означает, что несколько включений не приведут к множеству определений,VAL
которые будут конфликтовать во время связывания. В C без использованияstatic
вам нужно будет убедиться, что определен только один исходный файл, вVAL
то время как другие исходные файлы объявили егоextern
. Обычно это можно сделать, определив его (возможно, с инициализатором) в исходном файле и поместивextern
объявление в файл заголовка.static
переменные на глобальном уровне видны только в их собственном исходном файле, независимо от того, попали они туда через включение или были в основном файле.Примечание редактора: в C ++
const
объекты, в объявлении которыхstatic
нетextern
ключевых слов или, являются неявнымиstatic
.источник
В
static
иextern
метко на переменном файл в области видимости определить , являются ли они доступны в других единицах трансляции (т.е. других.c
или.cpp
файлов).static
обеспечивает внутреннюю связь переменной, скрывая ее от других единиц перевода. Однако переменные с внутренней связью могут быть определены в нескольких единицах перевода.extern
обеспечивает внешнюю связь переменной, делая ее видимой для других единиц перевода. Обычно это означает, что переменная должна быть определена только в одной единице перевода.Значение по умолчанию (если вы не указываете
static
илиextern
) - это одна из тех областей, в которых C и C ++ различаются.В C
extern
по умолчанию переменные в области файлов - это (внешняя связь). Если вы используете C,VAL
естьstatic
иANOTHER_VAL
естьextern
.В C ++ переменные в файловой области
static
по умолчанию являются (внутренняя связь), если они естьconst
, иextern
по умолчанию, если это не так. Если вы используете C ++, такVAL
иANOTHER_VAL
естьstatic
.Из черновика спецификации C :
Из черновика спецификации C ++ :
источник
Статичность будет означать, что вы получаете одну копию для каждого файла, но, в отличие от других, это совершенно законно. Вы можете легко проверить это на небольшом примере кода:
test.h:
static int TEST = 0; void test();
test1.cpp:
#include <iostream> #include "test.h" int main(void) { std::cout << &TEST << std::endl; test(); }
test2.cpp:
#include <iostream> #include "test.h" void test() { std::cout << &TEST << std::endl; }
Выполнение этого дает вам следующий результат:
источник
TEST
былиconst
, если LTO сможет оптимизировать его в одну ячейку памяти. Но-O3 -flto
из GCC 8.1 не вышло.const
переменные в C ++ имеют внутреннюю связь. Итак, использование неstatic
имеет никакого эффекта.ах
const int i = 10;
one.cpp
#include "a.h" func() { cout << i; }
two.cpp
#include "a.h" func1() { cout << i; }
Если бы это была программа на C, вы бы получили ошибку «множественное определение» для
i
(из-за внешней связи).источник
static
имеет эффект, который четко сигнализирует о намерении и осведомленности о том, что кодируете, что никогда не бывает плохим. Для меня это похоже на включениеvirtual
при переопределении: нам не нужно, но все выглядит намного более интуитивно понятным - и согласуется с другими декларациями - когда мы это делаем.Статическое объявление на этом уровне кода означает, что переменная видна только в текущей единице компиляции. Это означает, что эту переменную увидит только код в этом модуле.
если у вас есть файл заголовка, в котором объявлена статическая переменная, и этот заголовок включен в несколько файлов C / CPP, тогда эта переменная будет «локальной» для этих модулей. Будет N копий этой переменной для N мест, в которые включен заголовок. Они никак не связаны друг с другом. Любой код в любом из этих исходных файлов будет ссылаться только на переменную, объявленную в этом модуле.
В данном конкретном случае ключевое слово static, похоже, не приносит никакой пользы. Возможно, я что-то упускаю, но это, кажется, не имеет значения - я никогда раньше не видел ничего подобного.
Что касается встраивания, то в этом случае переменная, скорее всего, встроена, но только потому, что она объявлена как const. Компилятор может с большей вероятностью встроить статические переменные модуля, но это зависит от ситуации и компилируемого кода. Нет гарантии, что компилятор встроит «статику».
источник
const
,static
подразумевается и, следовательно, необязательно. Следствием этого является отсутствие подверженности множественным ошибкам определения, как утверждал Майк Ф.В книге C (бесплатно в Интернете) есть глава о связывании, в которой более подробно объясняется значение слова «статика» (хотя правильный ответ уже дан в других комментариях): http://publications.gbdirect.co.uk/c_book /chapter4/linkage.html
источник
Чтобы ответить на вопрос, "статика означает, что создается только одна копия VAL, если заголовок включен более чем одним исходным файлом?" ...
Нет . VAL всегда будет определяться отдельно в каждом файле, который включает заголовок.
В этом случае стандарты для C и C ++ действительно вызывают разницу.
Обратите внимание, что современные компоновщики могут жаловаться на ANOTHER_VAL, если заголовок включен в разные файлы (одно и то же глобальное имя определено дважды), и определенно будут жаловаться, если ANOTHER_VAL был инициализирован другим значением в другом файле.
Также необходимо учитывать тот факт, что обе переменные обозначены как const. В идеале компилятор всегда выбирал бы встроить эти переменные и не включать для них хранилище. Существует целый ряд причин, по которым можно выделить память. Я могу вспомнить ...
источник
Предполагая, что эти объявления находятся в глобальной области (т.е. не являются переменными-членами), тогда:
static означает «внутренняя связь». В этом случае, поскольку он объявлен как const, он может быть оптимизирован / встроен компилятором. Если вы опустите const, компилятор должен выделить память в каждой единице компиляции.
Если исключить static, по умолчанию будет установлена внешняя связь . Опять же , вы были спасены сопзИ Несса - компилятор может оптимизировать / рядное использование. Если вы отбросите константу, вы получите ошибку с несколькими определенными символами во время ссылки.
источник
Вы не можете объявить статическую переменную, не определив ее (это потому, что модификаторы класса хранения static и extern являются взаимоисключающими). Статическая переменная может быть определена в файле заголовка, но это приведет к тому, что каждый исходный файл, который включает файл заголовка, будет иметь свою собственную частную копию переменной, что, вероятно, не то, что планировалось.
источник
constПеременные по умолчанию статичны в C ++, но extern C. Поэтому, если вы используете C ++, не имеет смысла, какую конструкцию использовать.
(7.11.6 C ++ 2003 и в Apexndix C есть образцы)
Пример сравнения источников компиляции / ссылки как программы C и C ++:
bruziuz:~/test$ cat a.c const int b = 22; int main(){return 0;} bruziuz:~/test$ cat b.c const int b=2; bruziuz:~/test$ gcc -x c -std=c89 a.c b.c /tmp/ccSKKIRZ.o:(.rodata+0x0): multiple definition of `b' /tmp/ccDSd0V3.o:(.rodata+0x0): first defined here collect2: error: ld returned 1 exit status bruziuz:~/test$ gcc -x c++ -std=c++03 a.c b.c bruziuz:~/test$ bruziuz:~/test$ gcc --version | head -n1 gcc (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
источник
static
. Он сигнализирует о намерении / осведомленности о том, что делает программист, и поддерживает паритет с другими типами объявлений (и, fwiw, C), в которых отсутствует неявноеstatic
. Это похоже на включениеvirtual
и в последнее времяoverride
в объявления переопределяющих функций - не обязательно, но гораздо более самодокументируется и, в случае последнего, способствует статическому анализу.const
только переменную в заголовке сg++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)
. В результате получилось около 150 многократно определенных символов (по одному для каждой единицы перевода, в которую был включен заголовок). Я думаю , что нам нужны либоstatic
,inline
или анонимные / неназванные имена , чтобы избежать внешних связей.const int
внутри области пространства имен и в глобальном пространстве имен. И он скомпилирован и следует правилу «Объекты, объявленные как const и явно не объявленные как extern, имеют внутреннюю привязку». «... Может быть, в проекте по какой-то причине этот заголовок включен в скомпилированные исходники C, где правила совершенно другие.Статический не позволяет другому модулю компиляции извлекать эту переменную, так что компилятор может просто «встроить» значение переменной там, где оно используется, и не создавать для него хранилище памяти.
Во втором примере компилятор не может предположить, что какой-то другой исходный файл не будет его извлекать, поэтому он должен фактически сохранить это значение где-то в памяти.
источник
Статический не позволяет компилятору добавлять несколько экземпляров. Это становится менее важным с защитой #ifndef, но при условии, что заголовок включен в две отдельные библиотеки, а приложение связано, будут включены два экземпляра.
источник
static
«менее важным». и даже с обоими способами вы можете получить несколько внутренне связанных определений, что, вероятно, не предназначено.