-4

1) I know a non-const variable is external linkage by default (it's like it's been declared as external more or less) but i don't understand why can't i define a global variable such as int glbl_a in header

//test.h
#ifndef TEST_H
#define TEST_H
int glbl_a=0;   //wrong -> "multiple definition of `glbl_a`"    
static int st_glbl_a=1; //ok to initialize it in header!
extern int ext_glbl_a; //ok to declare it and define it in test.cpp
#endif

//test.cpp

#include "test.h"
using namespace std;

//st_glbl_a=22; i could use st_glbl_a but it wouldn't affect st_glbl_a in main cause of 'static'    
int ext_glbl_a=2; //definition of external gloabal non-const variable

//main.cpp

#include <iostream>
#include "test.h"
using namespace std;

extern int glbl_a; //declaration of glbl_a external
int main(){
 cout<<glbl_a;
}

the working version for this program is the one in which I define int glbl_a=0; in test.cpp only and declare extern int glbl_a; in main before using it in output (definition in test.h is just commented, that is there's nothing about glbl_a).

2)the working version doesn't work anymore if I group all definitions/declaretions into a namespace spread onto test.cpp and test.h (MyNamespace) cause of int glbl_a in test.cpp:

//test.h
#ifndef TEST_H
#define TEST_H
namespace MyNamespace{
//extern int glbl_a;
}
#endif

//test.cpp

#include "test.h"
using namespace std;
namespace MyNamespace{
 int glbl_a=0;
}

//main.cpp

#include <iostream>
#include "test.h"
using namespace std;

int main(){
 cout<<MyNamespace::glbl_a; //wrong -> "'glbl_a' is not a member of 'MyNaspace'"
}

it would work only if I de-comment declaration in test.h, but why?

Simson
  • 3,373
  • 2
  • 24
  • 38
  • Do you know how `#include` works? It is just a glorified copy-paste. If you know that - it is easy to understand why you get the error about multiple definitions of... – Algirdas Preidžius Jan 18 '18 at 00:55
  • you should add header guard to your code, and im not sure but i think extern in c++ is unnecessary – mariusz_latarnik01 Jan 18 '18 at 00:56
  • _@Luca_ Lookup _ODR_ please. –  Jan 18 '18 at 00:57
  • guys unlike it seems to be, i know that include is just a copy of header file, i know ODR rule and i always use #ifndef ... #endif preprocessor directive to avoid repating code. I just forgot to write it in this question and i'll correct, but i wrote on my PC. I asked this question because compiler says "multiple definition of `glbl_a`" even if i add header guard and that's what i find strange (this would avoid repating code but multiple declaration error remain) – Luca Ammendola Jan 18 '18 at 02:35

2 Answers2

3

Problem 1

Including a header effectively pastes the included file into the including file, producing one large file that is then compiled (and, typically, promptly deleted). This means that every including file now has its very own glbl_a. The compiler is happy, but when the linker attempts to put everything together, it finds many equally valid pretenders to the name glbl_a. The linker hates this and doesn't even try to figure out what you're trying to do. It simply spits out an error message and asks that you fix the problem.

Problem 2

test.cpp and main.cpp are different translation units. They compile to produce different, completely independent objects. Neither can see what's in the other, so the fact that MyNamespace::glbl_a exists in test.cpp is lost on main.cpp. When main.cpp is compiled, the compiler builds a list of identifiers declared in the file constructed from main.cpp and all of its included headers. MyNamespace::glbl_ais never declared as of when it is first used (or after for that matter) so the compiler spits out an error message.

Uncommenting the declaration in test.h means the compiler will find MyNamespace::glbl_a in main.cpp and will allow it's use. Since MyNamespace::glbl_a is defined in test.cpp the linker can find one-and-only-one MyNamespace::glbl_a and can successfully link.

extern does not allocate storage. Instead it is a promise to the compiler that the variable being declared will be fully defined and allocated somewhere else, maybe later in the file or in another file. The variable exists, somewhere, and compilation can continue. The linker will call you out as a liar if it cannot find a definition.

More details here: How does the compilation/linking process work?

More on extern: When to use extern in C++ and Storage class specifiers

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
  • thanks a lor, very clear. I already knew about ODR and how `#include` works, but i asked the first question beacuse of the error "multple definition" it send me although i used preprocessor directives in header (in the real code, on PC) – Luca Ammendola Jan 18 '18 at 02:55
  • @LucaAmmendola Include guards only prevent multiple includes within one translation unit, so the include guard cannot prevent multiple translation units with the same variables. Every time the compiler starts on a new file, it's a clean slate, the header is included, and if there's a variable in there, you get another object file containing the variable, and the linker freaks out. – user4581301 Jan 18 '18 at 03:25
  • ok thanks, i thought the compiler wouldnt include the header again even in a new translation unit. thank u a lot – Luca Ammendola Jan 18 '18 at 18:10
0

headers will be included by other files (more than one) thus if you define in header, it will be in each translation unit thus lead to "multiple definition"

WhatABeautifulWorld
  • 3,198
  • 3
  • 22
  • 30