wake-up-neo.com

Variablendeklarationen in Header-Dateien - statisch oder nicht?

Beim Refactoring einiger #defines Bin ich auf Deklarationen gestoßen, die in einer C++ - Headerdatei etwa so lauten:

static const unsigned int VAL = 42;
const unsigned int ANOTHER_VAL = 37;

Die Frage ist, welchen Unterschied, wenn überhaupt, die statische Aufladung macht. Beachten Sie, dass das mehrfache Einfügen der Header aufgrund des klassischen Tricks #ifndef HEADER#define HEADER#endif Nicht möglich ist (falls dies von Bedeutung ist).

Bedeutet statisch, dass nur eine Kopie von VAL erstellt wird, wenn der Header in mehreren Quelldateien enthalten ist?

86
Rob

static bedeutet, dass für jede Quelldatei, in der es enthalten ist, eine Kopie von VAL erstellt wird. Es bedeutet jedoch auch, dass mehrere Einschlüsse nicht zu mehreren Definitionen von VAL, die zum Zeitpunkt der Verknüpfung kollidieren. In C müssten Sie ohne static sicherstellen, dass nur eine Quelldatei VAL definiert ist, während die anderen Quelldateien extern deklarieren. Normalerweise würde man dies tun, indem man es (möglicherweise mit einem Initialisierer) in einer Quelldatei definiert und die extern -Deklaration in eine Header-Datei schreibt.

static - Variablen auf globaler Ebene sind nur in ihrer eigenen Quelldatei sichtbar, unabhängig davon, ob sie über ein Include dorthin gelangt sind oder in der Hauptdatei enthalten waren.


Anmerkung des Herausgebers: In C++ sind const - Objekte, deren Deklaration weder die Schlüsselwörter static noch extern enthält, implizit static.

102
Justsalt

Die Tags static und extern für dateibezogene Variablen bestimmen, ob sie in anderen Übersetzungseinheiten (d. H. Anderen .c oder .cpp Dateien).

  • static gibt die interne Verknüpfung der Variablen an und verbirgt sie vor anderen Übersetzungseinheiten. Variablen mit interner Verknüpfung können jedoch in mehreren Übersetzungseinheiten definiert werden.

  • extern gibt die variable externe Verknüpfung an und macht sie für andere Übersetzungseinheiten sichtbar. In der Regel bedeutet dies, dass die Variable nur in einer Übersetzungseinheit definiert werden muss.

Die Standardeinstellung (wenn Sie nicht static oder extern angeben) ist einer der Bereiche, in denen sich C und C++ unterscheiden.

  • In C sind dateibezogene Variablen standardmäßig extern (externe Verknüpfung). Wenn Sie C verwenden, ist VALstatic und ANOTHER_VAL ist extern.

  • In C++ sind dateibezogene Variablen standardmäßig static (interne Verknüpfung), wenn sie const sind, und extern, wenn dies nicht der Fall ist. Wenn Sie C++ verwenden, werden sowohl VAL als auch ANOTHER_VAL sind static.

Aus einem Entwurf der C-Spezifikation :

6.2.2 Verknüpfungen von Bezeichnern ... -5- Wenn die Deklaration eines Bezeichners für eine Funktion keinen Speicherklassenspezifizierer enthält, wird die Verknüpfung genau so bestimmt, als ob sie mit dem Speicherklassenspezifizierer extern deklariert worden wäre. Wenn die Deklaration eines Bezeichners für ein Objekt einen Dateibereich und keinen Speicherklassenspezifizierer aufweist, ist die Verknüpfung extern.

Aus einem Entwurf der C++ - Spezifikation :

7.1.1 - Speicherklassenspezifizierer [dcl.stc] ... -6- Ein Name, der in einem Namespace-Bereich ohne Speicherklassenspezifizierer deklariert wurde, ist extern verknüpft, sofern er nicht aufgrund einer vorherigen Deklaration intern verknüpft ist deklarierte const. Objekte, die als const deklariert und nicht explizit als extern deklariert wurden, sind intern verknüpft.

107
bk1e

Die statische Aufladung bedeutet, dass Sie eine Kopie pro Datei erhalten. Im Gegensatz zu anderen ist dies jedoch vollkommen legal. Sie können dies einfach mit einem kleinen Codebeispiel testen:

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

Wenn Sie dies ausführen, erhalten Sie folgende Ausgabe:

0x446020
0x446040

46
slicedlime

const Variablen in C++ sind intern verknüpft. Daher hat die Verwendung von static keine Auswirkung.

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

Wenn dies ein C-Programm wäre, würden Sie den Fehler "Mehrfachdefinition" für i erhalten (aufgrund einer externen Verknüpfung).

6
Nitin

Die statische Deklaration auf dieser Codeebene bedeutet, dass die Variable nur in der aktuellen Kompilierungseinheit sichtbar ist. Dies bedeutet, dass nur Code innerhalb dieses Moduls diese Variable sieht.

wenn Sie eine Header-Datei haben, die eine Variable als statisch deklariert, und dieser Header in mehreren C/CPP-Dateien enthalten ist, ist diese Variable für diese Module "lokal". Es gibt N Kopien dieser Variablen für die N Stellen, an denen der Header enthalten ist. Sie sind überhaupt nicht miteinander verwandt. Jeder Code in einer dieser Quelldateien verweist nur auf die Variable, die in diesem Modul deklariert ist.

In diesem speziellen Fall scheint das Schlüsselwort "static" keinen Nutzen zu bringen. Ich vermisse vielleicht etwas, aber es scheint keine Rolle zu spielen - ich habe noch nie zuvor so etwas gesehen.

In diesem Fall ist die Variable wahrscheinlich inline, aber nur, weil sie als const deklariert wurde. Der Compiler könnte führt mit größerer Wahrscheinlichkeit statische Modulvariablen ein, dies hängt jedoch von der Situation und dem Code ab, der kompiliert wird. Es gibt keine Garantie dafür, dass der Compiler 'Statics' einfügt.

5
Mark

Zur Beantwortung der Frage: "Bedeutet das Statische, dass nur eine Kopie von VAL erstellt wird, falls der Header in mehr als einer Quelldatei enthalten ist?" ...

NO . VAL wird in jeder Datei, die den Header enthält, immer separat definiert.

Die Standards für C und C++ bewirken in diesem Fall einen Unterschied.

In C sind dateibezogene Variablen standardmäßig extern. Wenn Sie C verwenden, ist VAL statisch und ANOTHER_VAL ist extern.

Beachten Sie, dass sich moderne Linker möglicherweise über ANOTHER_VAL beschweren, wenn der Header in verschiedenen Dateien enthalten ist (derselbe globale Name wurde zweimal definiert), und sich definitiv beschweren würden, wenn ANOTHER_VAL in einer anderen Datei mit einem anderen Wert initialisiert wurde

In C++ sind dateibezogene Variablen standardmäßig statisch, wenn sie const sind, und standardmäßig extern, wenn sie es nicht sind. Wenn Sie C++ verwenden, sind sowohl VAL als auch ANOTHER_VAL statisch.

Sie müssen auch berücksichtigen, dass beide Variablen als const bezeichnet werden. Im Idealfall würde der Compiler diese Variablen immer inline setzen und keinen Speicher für sie einschließen. Es gibt eine ganze Reihe von Gründen, warum Speicher zugewiesen werden kann. Einer, an den ich denken kann ...

  • debug-Optionen
  • adresse aus der Datei
  • der Compiler ordnet immer Speicher zu (komplexe const-Typen können nicht einfach inline gesetzt werden, wird also zu einem Sonderfall für Basistypen)
2
itj

Das C-Buch (kostenlos online) enthält ein Kapitel über Verknüpfungen, in dem die Bedeutung von "statisch" näher erläutert wird (obwohl die richtige Antwort bereits in anderen Kommentaren angegeben ist): http://publications.gbdirect.co .uk/c_book/chapter4/linkage.html

2
Jan de Vos

Sie können eine statische Variable nicht deklarieren, ohne sie ebenfalls zu definieren (dies liegt daran, dass sich die Speicherklassenmodifikatoren static und extern gegenseitig ausschließen). Eine statische Variable kann in einer Header-Datei definiert werden, dies würde jedoch dazu führen, dass jede Quelldatei, die die Header-Datei enthält, eine eigene private Kopie der Variablen hat, was wahrscheinlich nicht beabsichtigt ist.

2
Gajendra Kumar

Angenommen, diese Deklarationen sind global gültig (d. H. Keine Membervariablen), dann gilt Folgendes:

statisch bedeutet 'interne Verknüpfung'. In diesem Fall kann dies, da es als const deklariert ist, vom Compiler optimiert/inliniert werden. Wenn Sie const weglassen, muss der Compiler Speicher in jeder Kompilierungseinheit zuweisen.

Wenn statisch weggelassen wird, ist die Verknüpfung standardmäßig extern. Du wurdest wieder durch die const Ness gerettet - der Compiler kann die/Inline-Nutzung optimieren. Wenn Sie const löschen, wird zum Zeitpunkt der Verknüpfung ein mehrfach definierter Symbolfehler angezeigt.

1
Seb Rose

const Variablen sind in C++ standardmäßig statisch, aber extern C. Wenn Sie also C++ verwenden, hat dies keinen Sinn, welche Konstruktion Sie verwenden sollen.

(7.11.6 C++ 2003 und Apexndix C enthält Beispiele)

Beispiel beim Vergleichen von Compile-/Link-Quellen als C- und C++ - Programm:

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
1
bruziuz

Static verhindert, dass eine andere Kompilierungseinheit diese Variable extern verarbeitet, so dass der Compiler den Wert der Variablen an der Stelle, an der er verwendet wird, nur "einreihen" kann und keinen Speicher für sie erstellt.

In Ihrem zweiten Beispiel kann der Compiler nicht davon ausgehen, dass eine andere Quelldatei sie nicht extern bereitstellt, und muss diesen Wert daher irgendwo im Speicher ablegen.

0
Jim Buck