Podczas refaktoryzacji niektórych #defines
napotkałem deklaracje podobne do następujących w pliku nagłówkowym C ++:
static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;
Pytanie brzmi, jaką różnicę, jeśli w ogóle, zrobi statyczność? Zauważ, że wielokrotne dołączanie nagłówków nie jest możliwe z powodu klasycznej #ifndef HEADER
#define HEADER
#endif
sztuczki (jeśli to ma znaczenie).
Czy statyczny oznacza, że VAL
tworzona jest tylko jedna kopia , w przypadku gdy nagłówek jest zawarty w więcej niż jednym pliku źródłowym?
Odpowiedzi:
Te
static
środki, które będzie jedna kopiaVAL
stworzona dla każdego pliku źródłowego, to jest wliczone w. Ale oznacza to również, że wiele wtrącenia nie spowoduje w wielu definicjachVAL
, które zderzają się w momencie połączenia. W C, bez tegostatic
, musiałbyś upewnić się, że tylko jeden plik źródłowy jest zdefiniowany,VAL
podczas gdy inne pliki źródłowe go deklarująextern
. Zwykle można to zrobić, definiując go (prawdopodobnie z inicjatorem) w pliku źródłowym i umieszczającextern
deklarację w pliku nagłówkowym.static
zmienne na poziomie globalnym są widoczne tylko w ich własnym pliku źródłowym, niezależnie od tego, czy dostały się tam poprzez dołączenie, czy też znajdowały się w pliku głównym.Uwaga edytora: W C ++
const
obiekty bez słów kluczowychstatic
aniextern
w swojej deklaracji są niejawniestatic
.źródło
Znaczniki
static
i wextern
zmiennych o zasięgu pliku określają, czy są one dostępne w innych jednostkach tłumaczeniowych (tj. Inne.c
lub.cpp
pliki).static
daje zmiennej powiązanie wewnętrzne, ukrywając ją przed innymi jednostkami tłumaczeniowymi. Jednak zmienne z powiązaniami wewnętrznymi można definiować w wielu jednostkach tłumaczeniowych.extern
daje zmiennej link zewnętrzny, czyniąc ją widoczną dla innych jednostek tłumaczeniowych. Zwykle oznacza to, że zmienna musi być zdefiniowana tylko w jednej jednostce tłumaczeniowej.Wartość domyślna (jeśli nie określisz
static
lubextern
) jest jednym z tych obszarów, w których C i C ++ różnią się.W języku C zmienne o zasięgu pliku są
extern
domyślnie (łączenie zewnętrzne). Jeśli używasz C,VAL
jeststatic
iANOTHER_VAL
jestextern
.W C ++ zmienne o zasięgu pliku są
static
domyślnie (łączeniem wewnętrznym), jeśli takconst
, iextern
domyślnie, jeśli nie są. Jeśli używasz C ++, obaVAL
iANOTHER_VAL
sąstatic
.Z projektu specyfikacji C :
Z szkicu specyfikacji C ++ :
źródło
Statyczny oznacza, że otrzymasz jedną kopię na plik, ale w przeciwieństwie do innych osób powiedzieli, że jest to całkowicie legalne. Możesz to łatwo przetestować za pomocą małego przykładu kodu:
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; }
Uruchomienie tego daje następujący wynik:
źródło
TEST
takconst
, czy LTO byłoby w stanie zoptymalizować to w jednym miejscu pamięci. Ale-O3 -flto
z GCC 8.1 nie.const
zmienne w C ++ mają wewnętrzne powiązania. Tak więc używaniestatic
nie ma żadnego efektu.ah
const int i = 10;
one.cpp
#include "a.h" func() { cout << i; }
two.cpp
#include "a.h" func1() { cout << i; }
Gdyby to był program w C, wystąpiłby błąd „wielokrotnej definicji” dla
i
(z powodu zewnętrznego powiązania).źródło
static
ma taki efekt, że starannie sygnalizuje zamiar i świadomość tego, co kodujemy, co nigdy nie jest złe. Dla mnie jest to tak, jakby uwzględniaćvirtual
przy nadpisywaniu: nie musimy, ale rzeczy wyglądają o wiele bardziej intuicyjnie - i spójnie z innymi deklaracjami - kiedy to robimy.Deklaracja statyczna na tym poziomie kodu oznacza, że zmienna jest widoczna tylko w bieżącej jednostce kompilacji. Oznacza to, że tylko kod w tym module będzie widział tę zmienną.
jeśli masz plik nagłówkowy, który deklaruje zmienną jako statyczną, a ten nagłówek jest zawarty w wielu plikach C / CPP, wówczas zmienna będzie „lokalna” dla tych modułów. Będzie N kopii tej zmiennej dla N miejsc, w których znajduje się nagłówek. W ogóle nie są ze sobą spokrewnieni. Każdy kod w dowolnym z tych plików źródłowych będzie odwoływał się tylko do zmiennej zadeklarowanej w tym module.
W tym konkretnym przypadku słowo kluczowe „static” nie wydaje się przynosić żadnych korzyści. Mogę czegoś przeoczyć, ale wydaje się, że nie ma to znaczenia - nigdy wcześniej nie widziałem czegoś takiego.
Jeśli chodzi o wstawianie, w tym przypadku zmienna jest prawdopodobnie wstawiana, ale to tylko dlatego, że jest zadeklarowana jako stała. Kompilator może z większym prawdopodobieństwem wbudować statyczne zmienne modułu, ale zależy to od sytuacji i kompilowanego kodu. Nie ma gwarancji, że kompilator będzie zawierał „statykę”.
źródło
const
,static
jest domniemane, a zatem opcjonalne. Konsekwencją jest to, że nie ma podatności na wielokrotne błędy definicji, jak twierdził Mike F.Książka C (dostępna bezpłatnie online) zawiera rozdział o powiązaniach, w którym bardziej szczegółowo wyjaśnia się znaczenie słowa „statyczny” (chociaż poprawną odpowiedź podano już w innych komentarzach): http://publications.gbdirect.co.uk/c_book /chapter4/linkage.html
źródło
Aby odpowiedzieć na pytanie, „czy statyczny oznacza, że tworzona jest tylko jedna kopia VAL, w przypadku gdy nagłówek jest zawarty w więcej niż jednym pliku źródłowym?” ...
NIE . VAL będzie zawsze definiowane oddzielnie w każdym pliku zawierającym nagłówek.
Standardy C i C ++ powodują różnicę w tym przypadku.
Zauważ, że nowoczesne konsolidatory mogą narzekać na ANOTHER_VAL, jeśli nagłówek jest zawarty w różnych plikach (ta sama nazwa globalna zdefiniowana dwukrotnie) i na pewno skarżyliby się, gdyby ANOTHER_VAL został zainicjowany inną wartością w innym pliku
Musisz również wziąć pod uwagę fakt, że obie zmienne są oznaczone jako const. Idealnie byłoby, gdyby kompilator zawsze wybierał wstawianie tych zmiennych i nie zawierał dla nich pamięci. Istnieje wiele powodów, dla których można przydzielić pamięć. Te, które przychodzą mi do głowy ...
źródło
Zakładając, że te deklaracje mają zasięg globalny (tj. Nie są zmiennymi składowymi), to:
statyczny oznacza „wewnętrzne powiązanie”. W tym przypadku, ponieważ jest zadeklarowane jako const, może to zostać zoptymalizowane / wstawione przez kompilator. Jeśli pominiesz stałą, kompilator musi przydzielić pamięć w każdej jednostce kompilacji.
Pomijając statyczne wiązanie jest extern domyślnie. Ponownie, już został zapisany przez const Ness - kompilator może zoptymalizować / inline użytkowania. Jeśli upuścisz stałą , otrzymasz błąd wielokrotnie zdefiniowanych symboli w czasie łączenia.
źródło
Nie można zadeklarować zmiennej statycznej bez jej definiowania (dzieje się tak, ponieważ modyfikatory klasy magazynu static i extern wzajemnie się wykluczają). Zmienną statyczną można zdefiniować w pliku nagłówkowym, ale spowodowałoby to, że każdy plik źródłowy zawierający plik nagłówkowy miałby swoją prywatną kopię zmiennej, co prawdopodobnie nie było tym, co było zamierzone.
źródło
zmienne const są domyślnie statyczne w C ++, ale zewnętrzne C. Więc jeśli używasz C ++, nie ma sensu, jakiej konstrukcji użyć.
(7.11.6 C ++ 2003, a Apexndix C ma próbki)
Przykład porównania źródeł kompilacji / linków jako programu C i 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
źródło
static
. Sygnalizuje zamiar / świadomość tego, co robi programista i zachowuje zgodność z innymi typami deklaracji (i fwiw, C), które nie mają tego ukrytegostatic
. To jak włączanievirtual
i ostatniooverride
do deklaracji funkcji nadrzędnych - nie jest to konieczne, ale dużo bardziej samodokumentujące, aw przypadku tych drugich sprzyjające analizie statycznej.const
tylko zmiennej w nagłówku zg++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)
. W rezultacie powstało około 150 wielokrotnie zdefiniowanych symboli (po jednym na każdą jednostkę tłumaczeniową z dołączonym nagłówkiem). Myślę, że musimy albostatic
,inline
albo / namespace nienazwanych anonimowy uniknąć powiązań zewnętrznych.const int
w zakresie przestrzeni nazw i globalnej przestrzeni nazw. I jest skompilowany i postępuje zgodnie z zasadą "Obiekty zadeklarowane jako const, a nie jawnie zadeklarowane jako extern mają wewnętrzne powiązanie." ".... Może w projekcie z jakiegoś powodu ten nagłówek został włączony do skompilowanych źródeł C, gdzie reguły są zupełnie inne.Static uniemożliwia innej jednostce kompilacji externowanie tej zmiennej, tak aby kompilator mógł po prostu „wstawić” wartość zmiennej w miejscu jej użycia i nie tworzyć dla niej pamięci.
W drugim przykładzie kompilator nie może założyć, że jakiś inny plik źródłowy nie będzie go extern, więc musi faktycznie gdzieś przechowywać tę wartość w pamięci.
źródło
Statyczny zapobiega dodawaniu przez kompilator wielu wystąpień. Staje się to mniej ważne w przypadku ochrony #ifndef, ale zakładając, że nagłówek znajduje się w dwóch oddzielnych bibliotekach, a aplikacja jest połączona, uwzględnione zostaną dwie instancje.
źródło
static
„mniej ważnym”. a nawet z obydwoma możesz skończyć z wieloma wewnętrznie połączonymi definicjami, co prawdopodobnie nie jest zamierzone.