I read the page Definitions and ODR and I also read several relevant questions about ODR. Still don't understand why it is important.
For example, in file1.cpp:
int val = 1;
In file2.cpp:
int val = 2;
In main.cpp:
extern int val;
This case violates the ODR. But in real cases, will anyone do this? I mean one definition for a variable with the same name of the same type seems to be something natural. Even if someone did this, compiler or linker won't agree. I don't understand why we should care about ODR because if we write the wrong code, compiler or linker will give the error. Is it possible to violate ODR without being detected? Can anyone list some real cases or mistakes we often make?
I don't understand why should we care about ODR because if we write the wrong code, compiler will give error.
The ODR is the only thing allowing compilers to issue an error message. If we didn't have the ODR, compilers would be required to accept such code.
Is it possible to violate ODR while the code can be compiled?
Sure. Depending on the compiler, this may compile and link without any problems, or it may fail:
extern int var;
int main() {
int myvar = var;
}
Note the lack of a definition of var here.
Some compilers see that the local variable myvar is completely unused, and therefore remove the reference to var as well. Other compilers/linkers will complain about the missing definition of var. And for yet some others, it depends on which compiler flags you pass.
Another example:
// t1.cc
int var;
// t2.cc
long var;
Some compilers give int var and long var the same internal name and detect the conflict, other compilers include the type and don't see any conflict.
Small note on fixing the case
You can declare such variable as static in the translation unit, or within an anonymous namespace.
static global variables do have internal linkage and thus do not violate ODR.
file1.cpp
static int val = 1;
// OR
namespace
{
int val = 1;
}
file2.cpp
static int val = 2;
// OR
namespace
{
int val = 2;
}
Potential Case from Real Life
What if you have 2 DLLs, or SOs both featuring the same global variable and you load both shared libs in your application?
Related
I have 2 files, A.cpp and B.cpp, in a Win32 console application.
Both 2 files contain only the following 2 lines of code:
#include "stdafx.h"
int k;
When compiling it produces the error
Error 1 error LNK2005: "int k" (?a##3HA) already defined in A.obj
I don't understand what is happening.
Can someone please explain this to me?
Why this error?
You broke the one definition rule and hence the linking error.
Suggested Solutions:
If you need the same named variable in the two cpp files then You need to use Nameless namespace(Anonymous Namespace) to avoid the error.
namespace
{
int k;
}
If you need to share the same variable across multiple files then you need to use extern.
A.h
extern int k;
A.cpp
#include "A.h"
int k = 0;
B.cpp
#include "A.h"
//Use `k` anywhere in the file
In the Project’s Settings, add /FORCE:MULTIPLE to the Linker’s Command Line options.
From MSDN: "Use /FORCE:MULTIPLE to create an output file whether or not LINK finds more than one definition for a symbol."
If you want both to reference the same variable, one of them should have int k;, and the other should have extern int k;
For this situation, you typically put the definition (int k;) in one .cpp file, and put the declaration (extern int k;) in a header, to be included wherever you need access to that variable.
If you want each k to be a separate variable that just happen to have the same name, you can either mark them as static, like: static int k; (in all files, or at least all but one file). Alternatively, you can us an anonymous namespace:
namespace {
int k;
};
Again, in all but at most one of the files.
In C, the compiler generally isn't quite so picky about this. Specifically, C has a concept of a "tentative definition", so if you have something like int k; twice (in either the same or separate source files) each will be treated as a tentative definition, and there won't be a conflict between them. This can be a bit confusing, however, because you still can't have two definitions that both include initializers--a definition with an initializer is always a full definition, not a tentative definition. In other words, int k = 1; appearing twice would be an error, but int k; in one place and int k = 1; in another would not. In this case, the int k; would be treated as a tentative definition and the int k = 1; as a definition (and both refer to the same variable).
Assuming you want 'k' to be a different value in different .cpp files (hence declaring it twice), try changing both files to
namespace {
int k;
}
This guarantees that the name 'k' uniquely identifies 'k' across translation units. The old version static int k; is deprecated.
If you want them to point to the same value, change one to extern int k;.
Both files define variable k as an integer (int).
As a result, the linker sees two variables with the same name, and is unsure which one it should use if you ever refer to k.
To fix this, change one of the declarations to:
extern int k;
That means: "k is an integer, declared here, but defined externally (ie. the other file)."
Now there is only one variable k, that can be properly referred to by two different files.
And if you want these translation units to share this variable, define int k; in A.cpp and put extern int k; in B.cpp.
Presence of int k; in the header file causes symbol k to be defined within each translation unit this header is included to while linker expects it to be defined only once (aka One Definition Rule Violation).
While suggestion involving extern are not wrong, extern is a C-ism and should not be used.
Pre C++17 solution that would allow variable in header file to be defined in multiple translation units without causing ODR violation would be conversion to template:
template<typename x_Dummy = void> class
t_HeaderVariableHolder
{
public: static int s_k;
};
template<typename x_Dummy> int t_HeaderVariableHolder<x_Dummy>::s_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return t_HeaderVariableHolder<>::s_k;
}
With C++17 things become much simpler as it allows inline variables:
inline int g_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return g_k;
}
The linker tells you that you have the variable k defined multiple times. Indeed, you have a definition in A.cpp and another in B.cpp. Both compilation units produce a corresponding object file that the linker uses to create your program. The problem is that in your case the linker does not know whic definition of k to use. In C++ you can have only one defintion of the same construct (variable, type, function).
To fix it, you will have to decide what your goal is
If you want to have two variables, both named k, you can use an anonymous namespace in both .cpp files, then refer to k as you are doing now:
.
namespace {
int k;
}
You can rename one of the ks to something else, thus avoiding the duplicate defintion.
If you want to have only once definition of k and use that in both .cpp files, you need to declare in one as extern int k;, and leave it as it is in the other. This will tell the linker to use the one definition (the unchanged version) in both cases -- extern implies that the variable is defined in another compilation unit.
I have the following three files in my code (with most of the code removed. This is just to isolate the issue).
global.h:
//global.h
#ifndef GLOBAL_H
#define GLOBAL_H
extern const int ARRAYSIZEX;
extern const int ARRAYSIZEY;
extern const int ARRAYSIZEZ;
#endif //GLOBAL_H
global.cpp:
//global.cpp
#include "global.h"
const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;
main:
//main
#include "global.h"
using namespace std;
someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];
int main(int argc, char **argv)
{
//...
}
Compiling gives me three errors at the declaration of mySomeTypeArray.
error: array bound is not an integer constant before ']' token
I want to keep my global variable and array size definitions in global.h/cpp for this application, just for organization, so that all my configuration parameters are in one place. What's the proper way to achieve what I'm trying to do?
Thanks
Your declaration is failing because array sizes need to be evaluated at compile-time and your encapsulation scheme is actually hiding the values from the compiler. This is true because compilers work on individual translation units. While compiling main.cpp your compiler sees only extern const int ARRAYSIZEX thanks to the include statement, but not the value which is visible in a separate translation unit so it can't figure out the memory layout.
While const variables can used as array sizes in some contexts, the language provides the more appropriate constexpr qualifier which comes with a set of restrictions that enforce its compile-time evaluation and suitability for array sizes. I recommend always using it when appropriate because it will point you to the error in situations such as this. In this case, you would get a compiler error because an extern constexpr declaration is ill-formed which hints at the proper solution: to hold the values for compile-time constants directly inside the header file.
global.h
constexpr int ARRAYSIZEX = ...;
constexpr int ARRAYSIZEY = ...;
constexpr int ARRAYSIZEZ = ...;
main.cpp
#include "global.h"
someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];
The problem here is extern int x means "x is defined in another file, but don't worry about the particulars, all you need to know is it's an int". This is normally good enough, except when the compiler needs to know right there and then what x is.
Since that's defined in a whole other file it can't. That file must be compiled before it knows, and the result of that compilation, due to the way C++ works, can't impact the compilation of this file.
You'll need to declare that as a const int in a header if you want to share those values. extern int won't cut it.
Although this is a trivial example, there's really no reason to go down the extern road at all. Just define the values in the header file as regular const int.
Array size must be specified by an integer constant expression. A const int object can be used in an integer constant expression if and only if it declared with an initializer and that initializer is also an integer constant expression. Your ARRAYSIZE... variables do not satisfy that requirement. In main they are declared without an initializer. You cannot use ARRAYSIZE... variables as array sizes in main.
Unless you have a specific requirement to give these variables external linkage, simply declare (and define) them in the header as
const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;
These object will have internal linkage though, which is different from what your original variant attempts to do.
If really want to give them external linkage, declare them as inline extern const in the header
inline extern const int ARRAYSIZEX = 5;
inline extern const int ARRAYSIZEY = 2;
inline extern const int ARRAYSIZEZ = 4;
Since inline by itself prevents const from imposing internal linkage, extern is entirely optional in these declarations. And since inline const combination can be replaced with constexpr (as #M.M noted in the comments), you can achieve the same effect with just
constexpr int ARRAYSIZEX = 5;
constexpr int ARRAYSIZEY = 2;
constexpr int ARRAYSIZEZ = 4;
The problem here is that ARRAYSIZEX, ARRAYSIZEY and ARRAYSIZEZ are not the compile time constants. They are constants - so their values can't be changed but their values are not known to compiler.
In C++ the compilation process consists of 3 basic steps.
Preprocessing of all source files done by preprocessor.
Compilation for every translation unit (.cpp file) done by compiler. For every translation unit compiler creates an object file.
Linking of all object files done by linker. The output is an executable file.
In C++ the keyword extern for compiler means that the variable is 'somewhere' defined. The compiler doesn't know the variable's real address but by placing keyword extern it's assured that the variable really exists and the linker will be able to find its address by its name when creating the executable file.
The problem here is that compiler in step 2 wants to create the object file but it doesn't known how big the array is going to be because it doesn't know the value of these constants. Yes, linker in step 3 will finally find them when putting together all object files but it's too late for compiler. So it generates that error.
The solution is simple. Use already mentioned constexpr keyword and initialize all variables with initializers. The keyword constexpr marks compile time constants - constants that have to be initialized in initializers and are known to compiler.
Sorry for this type of question. But, I am very curious about the keyword extern in C\C++.
while searching for explanation for extern I got to know that extern tell the compiler that the variable or function is already defined in some other file or program.
But if this is a case then why we use extern?
as I tried some codes as follows:
extern int var;
int main(void)
{
var = 10;
return 0;
}
This code is giving me an error message as unresolved external symbol "int var" (?var##3HA).
and if I am using some code like:
extern int var;
int main(void)
{
int var = 10;
return 0;
}
It is not showing any error and gives value same as I have defined in main function.
So, Can any one help me about the behavior of extern?
I am little confused on this.
Please forgive me if it is not a valid question.
Thank you in Advance.
extern is used to refer to a variable in another translation unit ("source file").
For instance, your code in main.c looks like this:
extern int var;
int main(void)
{
var = 10;
return 0;
}
This code contains a declaration for an integer named var, but no definition, because extern explicitly says: "the definition for this is somewhere else"
You could define another source file, say, other.c:
int var = 0;
Then, after you add this new translation unit to your build command, your program will link fine, and the code in both files can operate on the shared var variable.
In the second version, you just override the declaration of the extern var with a local variable in your main function. Since the extern var is not (ODR-)used anymore, the linker does not require it, so your executable can build successfully.
The first version fails since you must also provide a definition of the extern-declared variable, i.e. by linking with some other C file that has
int var;
in its global scope.
The second one succeeds since the int var = 10; line in main() shadows the global declaration; this is generally a bad thing to be doing if you wanted the variable to be extern, i.e. shared among many C files. Of course, such sharing in itself is often quite bad too (see: global variables).
extern is used to indicate that a variable exists in another compilation unit. If you had:
main.cpp
extern int var;
int main(void)
{
var = 10;
return 0;
}
and:
var.cpp
int var;
Then you wouldn't have got the linker error as the linker would have hooked up the use of var in main.cpp with it's decleration in var.cpp
In your second example you've defined an extern int var but you never reference it. The int var = 10 is a completely different int var from the extern one. When they linker runs it notices that you never use the extern int var variable so doesn't bother to search for it.
I would suggest you must clear your understanding of definition and declaration of variable.
extern keyword is used when you want to say that the variable was allocated memory you want only to declare the variable.
When you declare the any variable using extern keyword compiler will try to find the definition of the same variable which can be present before or after declaration.
In the first case you are just declaring the variable not assigning it any memory i.e not defining variable.
you can go through this for better understanding of extern keyword.
If you have declared a global variable, let's say double myvar=5 in file1.cpp and you want to access that variable in file2.cpp, then in your file2.cpp you declare as
extern double myvar;
In C++, each file is called a compilation unit. For the file to be compiled to an object file, you need all variables declared however you do not need to initialize or do an assignment on those variables.
Returning to the example:
File1.cpp: double myvar=5 this is both a declaration and initialization. You can compile file1.cpp to file1.o.
File2.cpp: Assuming that you are using myvar somewhere in file2.cpp and also you want to access the value (maybe after a calculation) in file1.cpp. Therefore, in order to be able to compile file2.cpp into file2.o, you must declare extern double myvar, so that file2.cpp can be compiled. This will please the compiler and leaves the task to linker.
Now that you have compiled these files, you will have object files (translation units) named as (if you are using g++) file1.o and file2.o. Now it is linker's job to link them together and let file2.o to access myvar.
Declaring a variable extern tells the compiler that the actual instance of the variable is somewhere else. If you don't provide it somewhere else, the link stage will fail.
In your second example you don't refer to the extern declared variable at all. You declare a new one in the scope of your main function that shadows the global declaration.
I have 2 files, A.cpp and B.cpp, in a Win32 console application.
Both 2 files contain only the following 2 lines of code:
#include "stdafx.h"
int k;
When compiling it produces the error
Error 1 error LNK2005: "int k" (?a##3HA) already defined in A.obj
I don't understand what is happening.
Can someone please explain this to me?
Why this error?
You broke the one definition rule and hence the linking error.
Suggested Solutions:
If you need the same named variable in the two cpp files then You need to use Nameless namespace(Anonymous Namespace) to avoid the error.
namespace
{
int k;
}
If you need to share the same variable across multiple files then you need to use extern.
A.h
extern int k;
A.cpp
#include "A.h"
int k = 0;
B.cpp
#include "A.h"
//Use `k` anywhere in the file
In the Project’s Settings, add /FORCE:MULTIPLE to the Linker’s Command Line options.
From MSDN: "Use /FORCE:MULTIPLE to create an output file whether or not LINK finds more than one definition for a symbol."
If you want both to reference the same variable, one of them should have int k;, and the other should have extern int k;
For this situation, you typically put the definition (int k;) in one .cpp file, and put the declaration (extern int k;) in a header, to be included wherever you need access to that variable.
If you want each k to be a separate variable that just happen to have the same name, you can either mark them as static, like: static int k; (in all files, or at least all but one file). Alternatively, you can us an anonymous namespace:
namespace {
int k;
};
Again, in all but at most one of the files.
In C, the compiler generally isn't quite so picky about this. Specifically, C has a concept of a "tentative definition", so if you have something like int k; twice (in either the same or separate source files) each will be treated as a tentative definition, and there won't be a conflict between them. This can be a bit confusing, however, because you still can't have two definitions that both include initializers--a definition with an initializer is always a full definition, not a tentative definition. In other words, int k = 1; appearing twice would be an error, but int k; in one place and int k = 1; in another would not. In this case, the int k; would be treated as a tentative definition and the int k = 1; as a definition (and both refer to the same variable).
Assuming you want 'k' to be a different value in different .cpp files (hence declaring it twice), try changing both files to
namespace {
int k;
}
This guarantees that the name 'k' uniquely identifies 'k' across translation units. The old version static int k; is deprecated.
If you want them to point to the same value, change one to extern int k;.
Both files define variable k as an integer (int).
As a result, the linker sees two variables with the same name, and is unsure which one it should use if you ever refer to k.
To fix this, change one of the declarations to:
extern int k;
That means: "k is an integer, declared here, but defined externally (ie. the other file)."
Now there is only one variable k, that can be properly referred to by two different files.
And if you want these translation units to share this variable, define int k; in A.cpp and put extern int k; in B.cpp.
Presence of int k; in the header file causes symbol k to be defined within each translation unit this header is included to while linker expects it to be defined only once (aka One Definition Rule Violation).
While suggestion involving extern are not wrong, extern is a C-ism and should not be used.
Pre C++17 solution that would allow variable in header file to be defined in multiple translation units without causing ODR violation would be conversion to template:
template<typename x_Dummy = void> class
t_HeaderVariableHolder
{
public: static int s_k;
};
template<typename x_Dummy> int t_HeaderVariableHolder<x_Dummy>::s_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return t_HeaderVariableHolder<>::s_k;
}
With C++17 things become much simpler as it allows inline variables:
inline int g_k{};
// Getter is necessary to decouple variable storage implementation details from access to it.
inline int & Get_K() noexcept
{
return g_k;
}
The linker tells you that you have the variable k defined multiple times. Indeed, you have a definition in A.cpp and another in B.cpp. Both compilation units produce a corresponding object file that the linker uses to create your program. The problem is that in your case the linker does not know whic definition of k to use. In C++ you can have only one defintion of the same construct (variable, type, function).
To fix it, you will have to decide what your goal is
If you want to have two variables, both named k, you can use an anonymous namespace in both .cpp files, then refer to k as you are doing now:
.
namespace {
int k;
}
You can rename one of the ks to something else, thus avoiding the duplicate defintion.
If you want to have only once definition of k and use that in both .cpp files, you need to declare in one as extern int k;, and leave it as it is in the other. This will tell the linker to use the one definition (the unchanged version) in both cases -- extern implies that the variable is defined in another compilation unit.
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.