94

When refactoring away some #defines I came across declarations similar to the following in a C++ header file:

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

The question is, what difference, if any, will the static make? Note that multiple inclusion of the headers isn't possible due to the classic #ifndef HEADER #define HEADER #endif trick (if that matters).

Does the static mean only one copy of VAL is created, in case the header is included by more than one source file?

sth
  • 222,467
  • 53
  • 283
  • 367
Rob
  • 76,700
  • 56
  • 158
  • 197

12 Answers12

112

The static and extern tags on file-scoped variables determine whether they are accessible in other translation units (i.e. other .c or .cpp files).

  • static gives the variable internal linkage, hiding it from other translation units. However, variables with internal linkage can be defined in multiple translation units.

  • extern gives the variable external linkage, making it visible to other translation units. Typically this means that the variable must only be defined in one translation unit.

The default (when you don't specify static or extern) is one of those areas in which C and C++ differ.

  • In C, file-scoped variables are extern (external linkage) by default. If you're using C, VAL is static and ANOTHER_VAL is extern.

  • In C++, file-scoped variables are static (internal linkage) by default if they are const, and extern by default if they are not. If you're using C++, both VAL and ANOTHER_VAL are static.

From a draft of the C specification:

6.2.2 Linkages of identifiers ... -5- If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

From a draft of the C++ specification:

7.1.1 - Storage class specifiers [dcl.stc] ... -6- A name declared in a namespace scope without a storage-class-specifier has external linkage unless it has internal linkage because of a previous declaration and provided it is not declared const. Objects declared const and not explicitly declared extern have internal linkage.

bk1e
  • 23,871
  • 6
  • 54
  • 65
109

The static means that there will be one copy of VAL created for each source file it is included in. But it also means that multiple inclusions will not result in multiple definitions of VAL that will collide at link time. In C, without the static you would need to ensure that only one source file defined VAL while the other source files declared it extern. Usually one would do this by defining it (possibly with an initializer) in a source file and put the extern declaration in a header file.

static variables at global level are only visible in their own source file whether they got there via an include or were in the main file.


Editor's note: In C++, const objects with neither the static nor extern keywords in their declaration are implicitly static.

M.M
  • 138,810
  • 21
  • 208
  • 365
Justsalt
  • 1,886
  • 3
  • 17
  • 24
  • I'm a fan of the last sentence, incredibly helpful. I didn't up vote the answer 'cause 42 is better. edit: grammar – RealDeal_EE'18 Nov 01 '13 at 16:23
  • "The static means that there will be one copy of VAL created for each source file it is included in." That seems to imply that there would be two copies of VAL if two source files included the header file. I'm hoping that's not true, and that there is always a single instance of VAL, regardless of how many files include the header. – Brent212 Jul 16 '14 at 19:17
  • 4
    @Brent212 The compiler does not know whether a declaration/definition came from a header file or main file. So you hope in vain. There will be two copies of VAL if someone was silly and put a static definition in a header file and it got included in two sources. – Justsalt Jul 24 '14 at 20:26
  • 1
    const values have internal linkage in C++ – adrianN Apr 15 '16 at 14:27
50

The static will mean you get one copy per file, but unlike others have said it's perfectly legal to do so. You can easily test this with a small code sample:

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

Running this gives you this output:

0x446020
0x446040

slicedlime
  • 2,142
  • 1
  • 17
  • 16
  • I wonder if `TEST` were `const`, if LTO would be able to optimize it into a single memory location. But `-O3 -flto` of GCC 8.1 did not. – Ciro Santilli OurBigBook.com Dec 21 '18 at 10:45
  • It'd be illegal for it to do so - even if it's constant, static guarantees that each instance is local to the compilation unit. It could probably inline the constant value itself if used as a constant though, but since we take its address it has to return a unique pointer. – slicedlime Jan 17 '19 at 10:21
6

const variables in C++ have internal linkage. So, using static has no effect.

a.h

const int i = 10;

one.cpp

#include "a.h"

func()
{
   cout << i;
}

two.cpp

#include "a.h"

func1()
{
   cout << i;
}

If this were a C program, you would get 'multiple definition' error for i (due to external linkage).

sth
  • 222,467
  • 53
  • 283
  • 367
Nitin
  • 61
  • 1
  • 1
  • 2
    Well, using `static` has the effect that it neatly signals intent and awareness of what one is coding, which is never a bad thing. To me this is like including `virtual` when overriding: we don't have to, but things look a lot more intuitive - and consistent with other declarations - when we do. – underscore_d Jul 25 '16 at 07:09
  • You *might* get multiple definition error in C. It is undefined behaviour with no diagnostic required – M.M Nov 15 '17 at 02:40
5

The static declaration at this level of code means that the variabel is only visible in the current compilation unit. This means that only code within that module will see that variable.

if you have a header file that declares a variable static and that header is included in multiple C/CPP files, then that variable will be "local" to those modules. There will be N copies of that variable for the N places that header is included. They are not related to each other at all. Any code within any of those source files will only reference the variable that is declared within that module.

In this particular case, the 'static' keyword doesn't seem to be providing any benefit. I might be missing something, but it seems to not matter -- I've never seen anything done like this before.

As for inlining, in this case the variable is likely inlined, but that's only because it's declared const. The compiler might be more likely to inline module static variables, but that's dependent on the situation and the code being compiled. There is no guarantee that the compiler will inline 'statics'.

Mark
  • 10,022
  • 2
  • 38
  • 41
  • The benefit of 'static' here is that otherwise you're declaring multiple globals all with the same name, one for each module that includes the header. If the linker doesn't complain that's only because it's biting it's tongue and being polite. –  Sep 18 '08 at 14:14
  • In this case, due to the `const`, the `static` is implied and hence optional. The corollary is that there is no susceptibility to multiple definition errors as Mike F claimed. – underscore_d Jul 25 '16 at 07:11
2

The C book (free online) has a chapter about linkage, which explains the meaning of 'static' in more detail (although the correct answer is already given in other comments): http://publications.gbdirect.co.uk/c_book/chapter4/linkage.html

Jan de Vos
  • 3,778
  • 1
  • 20
  • 16
2

To answer the question, "does the static mean only one copy of VAL is created, in case the header is included by more than one source file?"...

NO. VAL will always be defined separately in every file that includes the header.

The standards for C and C++ do cause a difference in this case.

In C, file-scoped variables are extern by default. If you're using C, VAL is static and ANOTHER_VAL is extern.

Note that Modern linkers may complain about ANOTHER_VAL if the header is included in different files (same global name defined twice), and would definitely complain if ANOTHER_VAL was initialised to a different value in another file

In C++, file-scoped variables are static by default if they are const, and extern by default if they are not. If you're using C++, both VAL and ANOTHER_VAL are static.

You also need to take account of the fact that both variables are designated const. Ideally the compiler would always choose to inline these variables and not include any storage for them. There is a whole host of reasons why storage can be allocated. Ones I can think of...

  • debug options
  • address taken in the file
  • compiler always allocates storage (complex const types can't easily be inlined, so becomes a special case for basic types)
itj
  • 1,165
  • 1
  • 11
  • 16
  • Note: *In the abstract machine* there is one copy of VAL in each separate translation unit that includes the header. In practice the linker might decide to combine them anyway, and the compiler might optimize some or all of them out first. – M.M Nov 15 '17 at 02:41
1

You can’t declare a static variable without defining it as well (this is because the storage class modifiers static and extern are mutually exclusive). A static variable can be defined in a header file, but this would cause each source file that included the header file to have its own private copy of the variable, which is probably not what was intended.

Gajendra Kumar
  • 908
  • 2
  • 12
  • 28
  • *"... but this would cause each source file that included the header file to have its own private copy of the variable, which is probably not what was intended."* - Due to [static initialization order fiasco](https://stackoverflow.com/questions/tagged/static-order-fiasco), it may be required to have a copy in each translation unit. – jww Nov 12 '17 at 07:59
1

const variables are by default static in C++, but extern C. So if you use C++ this no sense what construction to use.

(7.11.6 C++ 2003, and Apexndix C has samples)

Example in compare compile/link sources as C and C++ program:

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
Konstantin Burlachenko
  • 5,233
  • 2
  • 41
  • 40
  • There _is_ sense in still including the `static`. It signals intent/awareness of what the programmer is doing and maintains parity with other types of declaration (and, fwiw, C) that lack the implicit `static`. It's like including `virtual` and lately `override` in declarations of overriding functions - not necessary, but a lot more self-documenting and, in the case of the latter, conducive to static analysis. – underscore_d Jul 25 '16 at 07:15
  • I'm absoultely agree. e.g. As for me in real life I always write it explicitly. – Konstantin Burlachenko Jul 25 '16 at 20:15
  • *"So if you use C++ this no sense what construction to use..."* - Hmm... I just compiled a project that used `const` only on a variable in a header with `g++ (GCC) 7.2.1 20170915 (Red Hat 7.2.1-2)`. It resulted in about 150 multiply defined symbols (one for each translation unit the header was included). I think we need either `static`, `inline` or an anonymous/unnamed namespace to avoid the external linkage. – jww Nov 12 '17 at 07:56
  • I tried baby-example with gcc-5.4 with declare `const int` inside namespace scope and in global namespace. And it's compiled and follow the rule " Objects declared const and not explicitly declared extern have internal linkage.""....Maybe in project in some reason this header included into C compiled sources, where the rules completely different. – Konstantin Burlachenko Nov 15 '17 at 01:48
  • @jww I uploaded example with linkage issue for C and no issues for C++ – Konstantin Burlachenko Nov 15 '17 at 01:57
1

Assuming that these declarations are at global scope (i.e. aren't member variables), then:

static means 'internal linkage'. In this case, since it is declared const this can be optimised/inlined by the compiler. If you omit the const then the compiler must allocate storage in each compilation unit.

By omitting static the linkage is extern by default. Again, you've been saved by the constness - the compiler can optimise/inline usage. If you drop the const then you will get a multiply defined symbols error at link time.

Seb Rose
  • 3,628
  • 18
  • 29
  • I believe the compiler must allocate space for a const int in all cases, since another module could always say "extern const int whatever; something(&whatever);" –  Sep 18 '08 at 14:04
0

Static prevents another compilation unit from externing that variable so that the compiler can just "inline" the variable's value where it is used and not create memory storage for it.

In your second example, the compiler cannot assume that some other source file won't extern it, so it must actually store that value in memory somewhere.

Jim Buck
  • 20,482
  • 11
  • 57
  • 74
-2

Static prevents the compiler from adding multiple instances. This becomes less important with #ifndef protection, but assuming the header is included in two seperate libraries, and the application is linked, two instances would be included.

Superpolock
  • 3,515
  • 7
  • 30
  • 24
  • assuming that by "libraries" you mean _translation units_, then no, include-guards do absolutely nothing to prevent multiple definitions, seeing as they only guard against repeated inclusions _within the **same** translation unit_. so, they do nothing whatsoever to make `static` "less important". and even with both, you can end up with multiple internally linked definitions, which probably isn't intended. – underscore_d Jul 25 '16 at 20:58