zmienne statyczne w funkcji wbudowanej


85

Mam funkcję, która jest zadeklarowana i zdefiniowana w pliku nagłówkowym. To jest problem sam w sobie. Gdy ta funkcja nie jest wstawiona, każda jednostka tłumacząca, która używa tego nagłówka, otrzymuje kopię funkcji, a kiedy są ze sobą połączone, są duplikowane. „Naprawiłem” to, wprowadzając funkcję w tekście, ale obawiam się, że jest to delikatne rozwiązanie, ponieważ o ile wiem, kompilator nie gwarantuje wbudowania, nawet jeśli określisz słowo kluczowe „inline”. Jeśli to nieprawda, popraw mnie.

Tak czy inaczej, prawdziwe pytanie brzmi: co dzieje się ze zmiennymi statycznymi wewnątrz tej funkcji? Ile kopii otrzymam?

Odpowiedzi:


106

Myślę, że czegoś tu brakuje.

funkcja statyczna?

Zadeklarowanie funkcji statycznej spowoduje, że będzie ona „ukryta” w jednostce kompilacji.

Nazwa mająca zasięg przestrzeni nazw (3.3.6) ma łącze wewnętrzne, jeśli jest nazwą

- zmienna, funkcja lub szablon funkcji, który jest jawnie zadeklarowany jako statyczny;

3.5 / 3 - C ++ 14 (n3797)

Gdy nazwa ma powiązania wewnętrzne, do jednostki, którą ona oznacza, można odnieść się za pomocą nazw z innych zakresów w tej samej jednostce tłumaczeniowej.

3.5 / 2 - C ++ 14 (n3797)

Jeśli zadeklarujesz tę statyczną funkcję w nagłówku, wszystkie jednostki kompilacji, w tym ten nagłówek, będą miały własną kopię funkcji.

Rzecz w tym, że jeśli wewnątrz tej funkcji znajdują się zmienne statyczne, każda jednostka kompilacji zawierająca ten nagłówek będzie miała również swoją własną, osobistą wersję.

funkcja inline?

Zadeklarowanie go inline sprawia, że ​​jest on kandydatem do wstawiania (w dzisiejszych czasach w C ++ nie znaczy to wiele, ponieważ kompilator będzie wbudowany lub nie, czasami ignorując fakt, że słowo kluczowe inline jest obecne lub nieobecne):

Deklaracja funkcji (8.3.5, 9.3, 11.3) ze specyfikatorem wbudowanym deklaruje funkcję wbudowaną. Specyfikator wbudowany wskazuje implementacji, że podstawianie w wierszu treści funkcji w punkcie wywołania ma być preferowane w stosunku do zwykłego mechanizmu wywoływania funkcji. Implementacja nie jest wymagana, aby wykonać to podstawienie w linii w momencie wywołania; jednakże, nawet jeśli to podstawianie w wierszu zostanie pominięte, pozostałe reguły funkcji wbudowanych, zdefiniowane w 7.1.2, powinny być nadal przestrzegane.

7.1.2 / 2 - C ++ 14 (n3797)

W nagłówku ma to interesujący efekt uboczny: wbudowaną funkcję można zdefiniować wiele razy w tym samym module, a linker po prostu połączy „je” w jeden (jeśli nie zostały one wstawione z powodu kompilatora).

W przypadku zmiennych statycznych zadeklarowanych wewnątrz standard wyraźnie podaje jedną i tylko jedną z nich:

Statyczna zmienna lokalna w zewnętrznej funkcji wbudowanej zawsze odnosi się do tego samego obiektu.

7.1.2 / 4 - C ++ 98 / C ++ 14 (n3797)

(funkcje są domyślnie extern, więc jeśli nie oznaczysz swojej funkcji jako statycznej, dotyczy to tej funkcji)

Ma to tę zaletę, że jest „statyczne” (tj. Można je zdefiniować w nagłówku) bez wad (istnieje najwyżej raz, jeśli nie jest wstawione)

statyczna zmienna lokalna?

Statyczne zmienne lokalne nie mają żadnych powiązań (nie można do nich odwoływać się z nazwy poza swoim zakresem), ale mają statyczny czas przechowywania (tj. Są globalne, ale ich konstrukcja i niszczenie podlegają określonym regułom).

statyczne + wbudowane?

Mieszanie inline i static będzie miało konsekwencje, które opisałeś (nawet jeśli funkcja jest wbudowana, zmienna statyczna wewnątrz nie będzie, a skończysz z tyloma zmiennymi statycznymi, ile masz jednostek kompilacji, w tym definicję funkcji statycznych ).

Odpowiedz na dodatkowe pytanie autora

Odkąd napisałem pytanie, wypróbowałem to w Visual Studio 2008. Próbowałem włączyć wszystkie opcje, które sprawiają, że VS działa zgodnie ze standardami, ale możliwe, że niektóre mi ominęły. Oto wyniki:

Gdy funkcja jest tylko „wbudowana”, istnieje tylko jedna kopia zmiennej statycznej.

Gdy funkcja jest „statyczna w wierszu”, kopii jest tyle, ile jest jednostek tłumaczeniowych.

Prawdziwe pytanie brzmi teraz, czy tak ma być, czy też jest to cecha charakterystyczna kompilatora Microsoft C ++.

Więc przypuszczam, że masz coś takiego:

void doSomething()
{
   static int value ;
}

Musisz zdać sobie sprawę, że zmienna statyczna wewnątrz funkcji, po prostu jest to zmienna globalna ukryta dla wszystkich oprócz zakresu funkcji, co oznacza, że ​​tylko funkcja, w której jest zadeklarowana, może do niej dotrzeć.

Umieszczenie funkcji niczego nie zmieni:

inline void doSomething()
{
   static int value ;
}

Będzie tylko jedna ukryta zmienna globalna. Fakt, że kompilator będzie próbował wstawić kod, nie zmieni faktu, że istnieje tylko jedna globalna zmienna ukryta.

Teraz, jeśli twoja funkcja jest zadeklarowana jako statyczna:

static void doSomething()
{
   static int value ;
}

Wtedy jest „prywatna” dla każdej jednostki kompilacji, co oznacza, że ​​każdy plik CPP, w tym nagłówek, w którym zadeklarowana jest funkcja statyczna, będzie miał swoją prywatną kopię funkcji, w tym własną prywatną kopię globalnej ukrytej zmiennej, a więc tyle zmiennych, ile istnieją jednostki kompilacji zawierające nagłówek.

Dodanie „inline” do funkcji „static” ze zmienną „static” wewnątrz:

inline static void doSomething()
{
   static int value ;
}

ma taki sam skutek, jak brak dodania tego słowa kluczowego „inline”, jeśli chodzi o zmienną statyczną wewnątrz.

Zatem zachowanie VC ++ jest poprawne, a Ty mylisz prawdziwe znaczenie słów „inline” i „static”.


Myślę, że brakuje ci ważnego punktu, o którym warto wspomnieć, że na etapie łączenia wszystkie te zmienne statyczne zadeklarowane w funkcji inline zostaną rozwiązane na jeden, czy się mylę?
user14416

1
Nie, ponieważ każda zmienna statyczna znajduje się w oddzielnej funkcji: pomimo faktu, że funkcje mają tę samą nazwę, mają wewnętrzne powiązania, a zatem nie są współdzielone w jednostkach tłumaczeniowych.
paercebal

1
@paercebal w inline void doSomething() { static int value ; }, funkcja ma połączenie zewnętrzne; jest to naruszenie ODR, jeśli pojawia się w nagłówku zawierającym dwie różne jednostki
MM,

@MM co masz na myśli? Twoja funkcja inlinenie może naruszać ODR.
Ruslan

@Ruslan to non-sequitur
MM

39

Uważam, że kompilator tworzy wiele kopii zmiennej, ale konsolidator wybiera jedną i sprawia, że ​​wszystkie inne odwołują się do niej. Podobne wyniki miałem, gdy próbowałem eksperymentować z utworzeniem różnych wersji funkcji wbudowanej; jeśli funkcja nie była w rzeczywistości wbudowana (tryb debugowania), wszystkie wywołania trafiały do ​​tej samej funkcji, niezależnie od pliku źródłowego, z którego zostały wywołane.

Pomyśl przez chwilę jak kompilator - jak mogłoby być inaczej? Każda jednostka kompilacji (plik źródłowy) jest niezależna od pozostałych i może być kompilowana oddzielnie; każdy musi zatem stworzyć kopię zmiennej, myśląc, że jest jedyna. Konsolidator ma możliwość przekraczania tych granic i dostosowywania odniesień zarówno dla zmiennych, jak i funkcji.


2
ODPOWIEDŹ, masz całkowitą rację w tym, co tutaj mówisz. Nie rozumiem, dlaczego ludzie odrzucają tę odpowiedź. Domyślam się tylko, że czytają aż „wiele kopii zmiennej”, a potem przestają! :( W każdym razie token (+1) ode mnie.
Richard Corden,

3
Kiedy ludzie pytają, co to kompilator, mają na myśli kompilator + konsolidator, ponieważ nie można uruchamiać plików obiektowych. Więc ta odpowiedź jest poprawna, ale całkowicie bez znaczenia.
Evan Dark

1
Ponieważ ludzie są ignorantami, jest to bardziej zaawansowane pytanie i wszyscy mają dokonać rozróżnienia w dyskusji.
Sogartar

13

Odpowiedź Marka Ransoma okazała się pomocna - że kompilator tworzy wiele kopii zmiennej statycznej, ale konsolidator wybiera jedną i wymusza ją we wszystkich jednostkach tłumaczeniowych.

Gdzie indziej znalazłem to:

Zobacz [dcl.fct.spec] / 4

[..] Funkcja wbudowana z łączem zewnętrznym powinna mieć ten sam adres we wszystkich jednostkach tłumaczeniowych. Statyczna zmienna lokalna w zewnętrznej funkcji wbudowanej zawsze odnosi się do tego samego obiektu. Literał tekstowy w zewnętrznej funkcji wbudowanej jest tym samym obiektem w różnych jednostkach tłumaczenia.

Nie mam kopii normy do sprawdzenia, ale zgadza się ona z moim doświadczeniem w badaniu zespołu w VS Express 2008


5

Tak ma być. „static” mówi kompilatorowi, że chcesz, aby funkcja była lokalna w jednostce kompilacji, dlatego chcesz, aby jedna kopia na jednostkę kompilacji i jedna kopia zmiennych statycznych na instancję funkcji.

„inline” używane do informowania kompilatora, że ​​funkcja ma być wbudowana; obecnie przyjmuje to po prostu jako „w porządku, jeśli istnieje kilka kopii kodu, po prostu upewnij się, że jest to ta sama funkcja”. Więc wszyscy współużytkują zmienne statyczne.

Uwaga: ta odpowiedź została napisana w odpowiedzi na odpowiedź zamieszczoną w oryginalnym plakacie.


1
Pyta o „zmienne statyczne” w „funkcji wbudowanej”, a nie zmienne w funkcji statycznej.
Richard Corden

Zgadzamy się z tym, ale masz rację: konieczna jest zmiana, aby odpowiedź znalazła się z powrotem w kontekście.
Raphaël Saint-Pierre

Ja też na to natknąłem się . Więc który z nich to jest? inlinepowoduje, że funkcja jest wstawiana lub można mieć wiele kopii?
Vassilis

@Vassilis oba mają rację, chociaż inlinenie powoduje inliningu, po prostu sugeruje to i pozwala na więcej niż jedną definicję (ale nie w tej samej jednostce kompilacji).
Raphaël Saint-Pierre

3

Odkąd napisałem pytanie, wypróbowałem to w Visual Studio 2008. Próbowałem włączyć wszystkie opcje, które sprawiają, że VS działa zgodnie ze standardami, ale możliwe, że niektóre mi ominęły. Oto wyniki:

Gdy funkcja jest tylko „wbudowana”, istnieje tylko jedna kopia zmiennej statycznej.

Gdy funkcja jest „statyczna w wierszu”, kopii jest tyle, ile jest jednostek tłumaczeniowych.

Prawdziwe pytanie brzmi teraz, czy tak ma być, czy też jest to ideowa synchronizacja kompilatora Microsoft C ++.


1
„Gdy funkcja jest„ statyczna w tekście ”,„ - Twój oryginalny post nie zawierał nic o tym. Powinieneś spodziewać się innych wyników, ponieważ statyczne dla funkcji ma inne znaczenie niż statyczne dla zmiennej. statyczny na funkcji oznacza, że ​​inne jednostki tłumaczeniowe nie zobaczą tej definicji.
Programista Windows

Nie mam pewności co do ustawień, ale w tym przypadku kompilator działa poprawnie. Możesz jednak chcieć dołączyć test jednostkowy, na wypadek gdybyś napotkał jakiś niezgodny kompilator w przyszłości.
Robert Gould

-1

Inlining oznacza, że ​​kod wykonywalny (instrukcje) jest wbudowany w kod funkcji wywołującej. Kompilator może to zrobić niezależnie od tego, czy o to poprosiłeś. Nie ma to wpływu na zmienne (dane) zadeklarowane w funkcji.


-2

Wierzę, że otrzymasz jedną na jednostkę tłumaczeniową. W rzeczywistości masz wiele wersji tej funkcji (i jej zadeklarowanej zmiennej statycznej), po jednej dla każdej jednostki tłumaczeniowej zawierającej nagłówek.


-2

Oprócz wszelkich problemów projektowych, wszystko to może oznaczać, ponieważ już z tym utknąłeś, powinieneś używać statycznego w tym przypadku, a nie inline. W ten sposób wszyscy mają te same zmienne. (Funkcja statyczna)


-2

Statyczny oznacza, że ​​jedna kopia jest rozpowszechniana w całym programie, ale inline oznacza, że ​​wymaga tego samego kodu przez kilka razy w tym samym programie, więc nie jest możliwe uczynienie zmiennej statyczną wewnątrz funkcji wbudowanej.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.