Use case for initialized extern variable - c++

I realized that I can define an extern variable, like:
source.cpp
extern int i = 42; // definition, can very well just remove the `extern`
main.cpp
#include <iostream>
extern int i; // declaration
int main()
{
std::cout << i << std::endl;
}
I can then compile and link the program,
g++ main.cpp source.cpp
and it runs and correctly displays 42. The warning I get is
warning: 'i' initialized and declared 'extern' (gcc)
warning: 'extern' variable has an initializer (clang)
Using int i = 42; in source.cpp has exactly the same overall effect.
My question: is there any non-trivial use case for variables defined extern (not just declared then defined in another translation unit)? Is such a definition even standard compliant?

The extern specifier is useful in conjunction with variables that would have internal linkage without it, such as constants at namespace scope:
a.cpp:
extern const int n = 10; // external linkage
b.cpp:
int main()
{
extern const int n; // finds the n defined in the other TU
return n;
}

Related

Why to add "extern" keyword if the global variable is external by default?

main.cpp
#include <iostream>
extern int g_x; // this extern is a forward declaration of a variable named g_x that is defined somewhere else
int main()
{
std::cout << g_x; // prints 2
return 0;
}
add.cpp
int g_x { 2 };
If I delete extern in main.cpp, then the code does not work. One the other hand, I don't need extern in add.cpp. Global variables are external by default, but still. Is it because external linkage is a "one-sided relation" between two entities that are linked in different files?
extern is different from external linkage. extern just means that you do a declaration, and that the variable is defined somewhere else.
If you remove extern, int g_x will be defined in main.cpp violating ODR (since you have it defined twice).

extern array goes undefined reference error, but works well for int

compile error :
[build] main.cpp:8: undefined reference to `pallete'
example code:
common.cpp
const unsigned char pallete[] = {0, 60, 100, 119};
int a = 1;
main.cpp
#include <iostream>
extern const unsigned char pallete[];
extern int a;
int main() {
std::cout << a << std::endl;
std::cout << pallete[0] << std::endl;
return 0;
}
You have to declare pallete extern in common.cpp. Right now main.cpp knows that it should "look for" pallete in separate file but common.cpp treats it as local due to being const as it says here https://en.cppreference.com/w/cpp/language/cv#Notes
Notes
The const qualifier used on a declaration of a non-local non-volatile non-template (since C++14)non-inline (since C++17) variable that is not declared extern gives it internal linkage. This is different from C where const file scope variables have external linkage.
extern const unsigned char pallete[] = {0, 60, 100, 119};
int a = 1;
Most likely your compiler does what mine is warning about:
Warnung: variable 'pallete' is not needed and will not be emitted
If you add your extern statement also in the common.cpp it should work.
If you declare something extern you should always do it in some header which is included by everyone who wants to use the variable.
extern can be added:
extern const unsigned char pallete[] = {0, 60, 100, 119};
than translation unit treats it as external despite being const...

why are const variables not stored in the symbol table when compiling object files? [duplicate]

My project consists of only two source files:
a.cpp:
const int n = 8;
b.cpp:
extern const int n;
int main()
{
// error LNK2001: unresolved external symbol "int const n" (?n##3HB)
int m = n;
}
I know there are several methods to make it work; however, I just wonder WHY it does't work?
It's because const implies internal linkage by default, so
your "definition" isn't visible outside of the translation unit
where it appears.
In this case, by far the best solution is to put the declaration
(extern int const n;) in a header file, and include that in
both a.cpp and b.cpp. The linkage is determined by the
first declaration the compiler sees, so the later definition in
a.cpp will have the correct (external) linkage.
Alternatively, you can force the linkage in the definition:
extern int const n = 8;
Despite the extern, this is still a definition; anything with
an initializer outside of a class definition is a definition.
const and constexpr variables in C++ have internal linkage (and thus aren't accessible in other compilation unit) if they aren't also declared extern (either in the definition or in a previous declaration).
In C, it isn't the case (well C hasn't constexpr) so your code is valid, and more you can put extern on a definition.
So if you want to write code which is both C and C++ (and the two declarations should probably come from the same header as James pointed out):
// a.cpp
extern const int n;
const int n = 8;
// b.cpp
extern const int n;
int main()
{
int m = n;
}
if you don't
// a.cpp
extern const int n = 8;
is also possible
Declare it extern in a.cpp and just use without extern in b.cpp:
a.h
extern const int n ;
a.cpp
#include "a.h"
...
const int n= 8
b.cpp:
#include "a.h"
...
int main()
{
int m = n;
}
To share a const object among multiple files, you must define the variable as extern.
To define a single instance of a const variable, we use the keyword extern on both its definition and declaration(s):
From these rules you just need to add the extern keyword in your definition. you already have it in declaration.
If the other answers here do not do the trick, it may be the case that you have your definitions in different namespaces... if the compilation passes, and you get an undefined symbol linker error:
check the namespace of the undefined symbol; that's the effective namespace for the extern const int n declaration.
ensure that's your effective namespace where you make the const int n = 8 definition.

External linkage and »extern "C"« block

I have an int ID, which I want to define in C++ and make available for C linkage (contrived case for the sake of simplicity):
/* i.h */
#ifdef __cplusplus
extern "C" {
#endif
extern int ID;
#ifdef __cplusplus
}
#endif
Here's a C and a C++ program using the int:
/* m.cpp */
#include "i.h"
#include <iostream>
int main() { std::cout << ID << std::endl; }
/* m.c */
#include "i.h"
#include <stdio.h>
int main() { printf("%d\n", ID); }
Now what I'm wondering is the syntax of extern "C" and/or extern. Here's how int ID can and cannot be defined:
/* i.cpp */
// const int ID = 88; // no C linkage, obviously, LNK2019/1120
// extern "C" const int ID = 88; // provides C linkage
// extern "C" { const int ID = 88; } // no C linkage, surprisingly, LNK2019/1120
// extern "C" { extern const int ID = 88; } // C linkage restored
Compiling:
cl /nologo /W4 m.cpp i.cpp /MD /EHsc
cl /nologo /W4 m.c i.cpp /MD
What I don't understand is the syntax when extern "C" is used with a {block}. I have to repeat the extern, whereas with the blockless form of extern "C" I do not. Is this just a syntax quirk or is there more to it?
I stumbled upon this issue on page 98 of Inside COM by Dale Rogerson. There is a code listing with the nested extern and a comment intended to clarify (but which I don't understand):
#include <objbase.h>
extern "C"{
extern const IID IID_IX = {0x32bb8320, 0x41b, 0x11cf, ...};
// The extern is required to allocate memory for C++ constants.
}
Can anyone explain the internal extern?
The inner extern means what expected: "the symbol is defined somewhere else, it has external linkage, don't allocate space for it in this unit (unless defined here as well)".
The outer extern "C" { ... } has maybe a bit misleading syntax, it only tells the compiler "if you are creating names of any symbols with external linkage inside this block, use traditional C naming (C language linkage) instead of C++ name mangling (C++ language linkage) for them". But it does not specify that all things inside the block are "defined somewhere else" (have external linkage) -- they are still declared with the default internal linkage. That's why you need the inner extern as well.
The one-line variant extern "C" <variable declaration> is just a shorthand, as you probably want to define an external variable if you care about its cross-unit symbol name.
(As a side-note, I would include i.h also in i.cpp, that way I wouldn't have to remember to mess with extern "C" any more in the implementation.)
The problem is that const and extern are warring with each other. const means (among other things), "Make this definition internal, unless there's an explicit extern on it.". When the definition is in an extern "C" block, there's no extern directly on the definition, so the "implicit internal" from the const takes precedence. The other definitions all have an extern directly on the definition, so it becomes external.
Here's how int ID can and cannot be defined:
Careful! extern "C" has different effects in different contexts.
N3797 §7.5/7:
A declaration directly contained in a linkage-specification is treated
as if it contains the extern specifier (7.1.1) for the purpose of
determining the linkage of the declared name and whether it is a
definition. Such a declaration shall not specify a storage class.
extern "C" int i; // declaration
extern "C" {
int i; // definition
}
So the second line in your snippet is just a declaration whilst your third line is also a definition. This changes what effect the linkage-specification has on the declarations - you can't give a name defined in a C++ translation unit C linkage. You have to make it a pure declaration.

Why does "extern const int n;" not work as expected?

My project consists of only two source files:
a.cpp:
const int n = 8;
b.cpp:
extern const int n;
int main()
{
// error LNK2001: unresolved external symbol "int const n" (?n##3HB)
int m = n;
}
I know there are several methods to make it work; however, I just wonder WHY it does't work?
It's because const implies internal linkage by default, so
your "definition" isn't visible outside of the translation unit
where it appears.
In this case, by far the best solution is to put the declaration
(extern int const n;) in a header file, and include that in
both a.cpp and b.cpp. The linkage is determined by the
first declaration the compiler sees, so the later definition in
a.cpp will have the correct (external) linkage.
Alternatively, you can force the linkage in the definition:
extern int const n = 8;
Despite the extern, this is still a definition; anything with
an initializer outside of a class definition is a definition.
const and constexpr variables in C++ have internal linkage (and thus aren't accessible in other compilation unit) if they aren't also declared extern (either in the definition or in a previous declaration).
In C, it isn't the case (well C hasn't constexpr) so your code is valid, and more you can put extern on a definition.
So if you want to write code which is both C and C++ (and the two declarations should probably come from the same header as James pointed out):
// a.cpp
extern const int n;
const int n = 8;
// b.cpp
extern const int n;
int main()
{
int m = n;
}
if you don't
// a.cpp
extern const int n = 8;
is also possible
Declare it extern in a.cpp and just use without extern in b.cpp:
a.h
extern const int n ;
a.cpp
#include "a.h"
...
const int n= 8
b.cpp:
#include "a.h"
...
int main()
{
int m = n;
}
To share a const object among multiple files, you must define the variable as extern.
To define a single instance of a const variable, we use the keyword extern on both its definition and declaration(s):
From these rules you just need to add the extern keyword in your definition. you already have it in declaration.
If the other answers here do not do the trick, it may be the case that you have your definitions in different namespaces... if the compilation passes, and you get an undefined symbol linker error:
check the namespace of the undefined symbol; that's the effective namespace for the extern const int n declaration.
ensure that's your effective namespace where you make the const int n = 8 definition.