Understanding const variables being "local to a file"

Hello,

In c++ primer 5th ed, it mentions that const variables are local to a file because the compiler will replace, in code, a const variable's name with its initialised value. This is why const variables are local to a file, because the initialiser only exists in the file where the original definition of the variable is.

The book then states that I can write the exact same const variable name and definition in multiple files, unlike normal unconst variables where I would get a linker error.

It seems to suggest that I must rewrite the same const variable definition in multiple files if I want to use it in multiple files, or I must use the "extern" keyword to share between files.

But this isn't true - I tested this by putting a const variable in a header file, which I can access in main. This is without writing "extern".

Can someone please explain the situation, and why my book is saying to write extern when it doesn't seem to be required? I don't feel like I understand the whole const being "local to a file" thing at all.

Thanks
The subjects you're asking about are known as internally linkage and external linkage.

What you're calling "local to a file" refers to the subject of translation units. Essentially each CPP file in a project is a translation unit. Some may call it a compilation unit. It is a single object code output from the compiler. "Local to a file" refers to "local to a translation unit", or internally linked in a translation unit.

A const int is, by default, given internal linkage. This means that it is considered private to the translation unit, and the linker makes no effort to provide a means for other translation units to access them.

External linkage is the opposite, where the linker works to provide access to variables or functions in all translation units as it assembles the entire executable or library.

Now, here's where things can get a tad confusing.

Think about a const int declaration, which includes a definition like:

const int A = 5;

By default, A has internal linkage. If it's at the top of a CPP file, it is rather obviously in a single translation unit. Importantly, "A" will not be known to other translation units at link time, so if other translation units have a different use for "A", the linker has no complaint. For example, in a second CPP file:

const int A = 7;

In this situation, the value of the const integer in the first CPP file would be 5, but would be 7 in the second CPP file, and both are internally linked, meaning the linker has no reason to complain.

This is what you're describing as "local to a file".

Now, what difference is it if the const int is declared in a header, instead?

Virtually none. Accept, of course, that they would be defined as the same constant value (let's say 5, as the first example above). As such, it is no different for that const int to be defined in the header, or to be used independently in two different translation units.

However, they are two separate constants. The linker has no concern for them having identical names (both "A"), and does not see a conflict either way, and for the exact same reasons.

Now, consider what the book is saying by declaring them to be extern.

By declaring:

extern const int A = 5;

This informs all translation units that this is given external linkage. The linker will insist they be resolved into one and only one integer, the same integer viewed by all translation units.

While the code operates the same in either case, and by effect you see no difference, this enforces that the linker will be sure it is the same value in all translation units. You could not, for example, have an "A" in one translation unit that is 5, while another translation unit might define it as 7.

In the non-extern version, in a header file, basically there are multiple copies of the constant integer used in the various translation units, conceivably wasting space.

An extern would resolve to only one integer for all translation units, conceivably saving space.

That said, optimizers generally treat constants like this as an inline emission of a value within an instruction. That is, it is as if you typed "5" (or whatever value) for every use of that constant throughout the code.

Put another way, it may be that the compiler never generates an actual integer for such a constant, where it doesn't occupy run time RAM, but is "embedded" as static information either in every case it is used, or as some constant bit of data that's part of the code's data segment.

A "regular" integer is going to be give storage, primary so it could be changed during runtime. Constants might not.

Last edited on
Thanks for taking the time to write this, you've completely helped me understand this now, much appreciated.
But this isn't true - I tested this by putting a const variable in a header file, which I can access in main. This is without writing "extern".

In case it got lost in the wealth of good information that Niccolo supplied:

Putting something in a header file is exactly the same as putting it in each source file that includes the header.

That's exactly what the #include statement does - it tells the preprocessor to take all the code that's in the header file, and insert it into the source file that includes it.
Last edited on
@Niccolo, I think you should write a blog. You always supply lots of great info.
Last edited on
Topic archived. No new replies allowed.