Is a extern variable with initialization really a definition? - c++

I know a variable with initialization is a definition no matter if it is extern. However, since extern int x=1; is a definition, why doesn't the compiler throw redefinition error running the following code?
//ok,no error
int x;
extern int x=1;
int main()
{
}
//however,this is an error
extern int x=1;
int x;
int main()
{
}
Why can this happen? Is there any difference?
Update:You are right.When I compile with VS 2013,there is no error.Now I tried with gcc on linux and got an redefinition error just as I expected.
Just as #Bo Persson said,it's all about tentative definition in C. And when I change the file suffix to .c, gcc takes it as a warning rather than an error and it can be compiled.

Firstly, both of your snippets are ill-formed - please use a modern compiler to try them out.
first snippet on wandbox
second snippet on wandbox
When declaring a variable in global scope, it is automatically zero-initialized. This is the reason you're getting a redefinition error - x is already defined to zero.
The following snippet will compile:
extern int x;
int x;
int main()
{
}

Related

Extern Variable - Multiple Files

When I redefine an extern variable within another file with a different type, the VS compiler is not giving an error message. As far as I know, it should raise an error since it was globally defined as an extern in another file. What is the reason for this behavior?
source1.cpp
extern int x;
source2.cpp
int x = 5;
test.cpp
#include <iostream>
double x = 455;
int main()
{
std::cout << x; // writes 455
}
One Definition Rule
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.

Rules regarding using declarations c++

After reading the accepted answer from this question, I thought I understood why the program failed, since the using directive does not actually declare the entity i in the region. However names introduced by a using declaration can be used just like any other name and acts like a declaration.
With GCC, this fails
#include <iostream>
namespace X { int i = 1; }
using X::i;
int main() {
extern int i;
i = 2;
std::cout<<i;
}
But this is accepted
#include <iostream>
int i;
int main() {
extern int i;
i = 2;
std::cout<<i;
}
Technically, the example you've given does compile, but it fails to link. The issue is the line
extern int i;
What you're telling the compiler/linker here is "there will be a variable i defined somewhere else in the program, so, compiler, don't worry if you can't find the definition. Linker, I expect you to find the definition of i once you have all of the object files and link that in here."
We can see this in action using compiler explorer:
Without the extern declaration
With the extern declaration
In the second case, the declaration of i "shadows" the X::i which is visible at global scope, so we get the instruction
mov DWORD PTR i[rip], 2
whereas without the extern declaration, we get
mov DWORD PTR X::i[rip], 2
though I'm not totally sure on the shadowing part, as neither gcc nor clang warns about that with -Wshadow. In any case, we see now why the second example fails to link: the linker is trying to find the definition of i to use here, and while X::i is defined at global scope, i is not.

What if using extern declaration inside the function?

I've tried the following code and got an error.
int main()
{
//this will cause redefinition error
extern int x;
int x=2;
}
I've seen some answers about extern such as
When to use extern in C++
Defining extern variable in main() vs. globally
and got an concept,but I am still wondering what does the compiler do in this case. Can extern be used(legal) inside some function?
update:
More specifically, since extern int x is just a declaration,why can't I define int x? Does the compiler take extern int x as a definition?
but I am still wondering what does the compiler do in this case. Can extern be used(legal) inside some function?
it can, but you must not redeclare variable as you have in your code. So this is a valid example:
int main()
{
//this will cause redefinition error
extern int x;
x=2;
}
int x;
Of course it can be used, don't define another x inside the function:
int main()
{
extern int x;
x=2;
}
As the others have answered, yes, you can use it in your function as long as you don't declare another variable with that name.
To your question of what the compiler does, dreamlax's answer to the question you linked, handles it pretty well. The compiler doesn't need to do/know anything other than what it's type is so that it knows how it can be used. The linker will see that it's an extern and know that it needs to go find where it is actually declared.
This MSDN link provides more general info on externs and what Microsoft does in VS 2015.

Do inline namespace variables have internal linkage? If not, why does the code below work?

This question is directly related to this one. Consider the code:
#include <iostream>
inline namespace N1
{
int x = 42;
}
int x = 10;
int main()
{
extern int x;
std::cout << x; // displays 10
}
It displays 10. If I remove the extern int x; declaration then we get an ambiguity compiler time error
error: reference to 'x' is ambiguous
Question: why does the code work with the extern int x declaration work, and why does it stop working when I remove it? Is it because inline namespace variables have internal linkage?
No. There is no provision in [basic.link] that would cause x to have internal linkage. Specifically, "All other namespaces have external linkage.", and "other" refers to "not unnamed". Perhaps you were thinking of unnamed namespaces?
No, the code works because to avoid breaking existing C code, extern int x; has to work the same way in did in C, in other words creating a local extern to a global namespace (that's all we had in C) variable. Then when you use it later the locally declared extern removes any possible ambiguity.

extern keyword usage

I have three programs in which I am using extern keyword. I am not able to understand the result. Below are three examples:
Example 1: I was expecting that below code will give compilation error that multiple declaration of k. But it works fine?
int k; //works fine
extern int k = 10;
void main()
{
cout<<k<<endl;
getchar();
}
Example 2: When I am trying to initialize "k" in above example compiler gives error. Why?
int k = 20; //error
extern int k = 10;
void main()
{
cout<<k<<endl;
getchar();
}
Example 3: In this example I changed the order of definitions mentioned in example 1. When I compile this code I am getting errors. Why?
extern int k = 10;
int k; //error
void main()
{
cout<<k<<endl;
getchar();
}
Example 2: You are trying to initialize a global variable twice, with two different values. This is the error.
Example 3: You first declare an extern variable, and then define a variable with the same name in the same compilation unit. This is not possible.
You should use **extern** keyword the way it meant to be used which is to reference something that is out of the current scope.
You should also follow the rules that works in every compiler and not try to use anomalies of some compiler, your examples gave me lot of errors in my own compiler.
Rules are:
Use extern to reference and not to define a variable.
Use some sort of convention for external naming. I put all my externals as capitals, so when I see something like MYVAR I know its a global.
Put all of your externals in an include header(.h) file and do an #include in your cpp files, this way is more convenient and helps to unclutter your source code.
See this example where I use all 3 rules:
My module1.cpp file:
unsigned short int AGLOBAL = 10; // definer and initializer
void MyFunc(void)
{
AGLOBAL+=1; // no need to include anything here cause is defined above
// more .....
}
My Header file globals.h:
// this is to include only once
#ifndef MYH
#define MYH
extern unsigned short int AGLOBAL; // no value in here!
#endif
Other module2.cpp file:
#include globals.h
char SomeOtherFunc(void)
{
AGLOBAL+=10; // ok cause its declared by globals.h
// do more....
}
The use of extern keyword is to tell the compiler that:
The variable is defined externally.
The first program should give you an error. Which compiler are you using? BTW, void main() is not standard. Neither in C nor in C++.
Your compiler is sloppy. Compiling this trivial variation (including the header and using declaration), I get:
$ cat xxx.cpp
#include <iostream>
using namespace std;
int k; //works fine
extern int k = 10;
void main()
{
cout<<k<<endl;
getchar();
}
$ g++ -c xxx.cpp
xxx.cpp:5:12: warning: ‘k’ initialized and declared ‘extern’ [enabled by default]
xxx.cpp:5:12: error: redefinition of ‘int k’
xxx.cpp:4:5: error: ‘int k’ previously declared here
xxx.cpp:7:11: error: ‘::main’ must return ‘int’
$ g++ --version
g++ (GCC) 4.6.0
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$
Which compiler are you using?
int k;
extern int k = 10;
This is ok in C, where you can have a "tentative definition" in addition to the real one. In C++ it is not allowed. There it is an attempt to declare the same variable twice.
My C compiler also warns me that having both extern and an initialization at the same time is unusual.
int k = 20; //error
extern int k = 10;
This attempts to give k two different values, which of course doesn't work.
extern int k = 10;
int k; //error
This seems to be equivalent to case 1. It is not allowed in C++, but seems to be acceptable in C99.
Case 1: Gives an Redefinition Error in c++(gcc-4.3.4)
Case 2: Gives an Redefinition Error in c++(gcc-4.3.4)
Case 3: Gives an Redefinition Error in c++(gcc-4.3.4)
In all the three cases You try to define a variable named k with external linkage and you try to define another variable named k in the same scope, this generates a Redefinition error.
Reference:
C++ Standard: 3.1 Declarations and definitions
3 [Example: all but one of the following are definitions:
int a; // defines a
extern const int c = 1; // defines c
int f(int x) { return x+a; } // defines f and defines x
......
In the above snippet a & c both are definitions.
If your compiler does not give you an error for all the three cases, then it is broken as far as C++ is concerned.
I understand Examples 2 and 3. But the fact that Example 1 was compiled is so weird. gcc would never compiled such code.
For the third one, you really want this
extern int k = 10;
extern int k; //okay: this is just a declaration.
// extern int k = 4; re-define is no good.
void main()
{
cout<<k<<endl;
getchar();
}
You can only define a variable once. You may declare as many times as you like, however.
To indicate a bit further, int i; is both declaration and definition. Often time intiizliation is thought as "definition". For an autotmatic variable, both declaration and definition is done in one single statement.
So when we define int k; memory has been allocated whose name reference is "k". Hence, linker will complain when you try to redefine it.
int k;
extern int k = 3; // already defined in the previous statement
Hence, this is also a compilation error
extern int k = 3;
int k; // trying to redefine again - bad
This is probably applied to only in C++. I am not familiar with C, therefore, I can't speak of C. In C++, even my solution will complain, but will not throw error at it.
Please judge me and correct my mistakes. I am learning also.
First, a nit pick. You are talking about global variable resolution. This is the job of the linker and not the compiler. While it no real implications since all compiler suites are usually executed with the linker as well.
What you should know about global variables is that they are of two kinds: weak and strong. Since there can only be one variable with a given name, if there are multiple definitions, the linker must figure out which one to use. If there are multiple string definitions, then there is an error. If there is a single strong definition then it is chosen as the canonical one and all other definitions refer to it. If there are only weak definitions then one of them gets promoted.
Any declaration where the variable has a value assigned to it is considered strong. Also, if there is no assignment then the extern keyword will force the variable to refer to another variable if one exists (in effect making the weakest of the weak).
Case 1: You have a weak declaration followed by a strong one. This is cool.
Case 2: You have two strong declarations. Error!
Case 3: You have a strong declaration followed by a weak one. I'm not actually sure why this one fails. If I were to guess I would say that the second declaration is being treated as strong by the the linker.