George Newton
George Newton

Reputation: 3293

Extern makes no difference

I am defining a global variable in test2.h

#ifndef TEST2_H
#define TEST2_H

int test_var;

void use_it(void);

#endif

and defining it again in two different files, test.c

#include <stdio.h>
#include "test2.h"

int test_var;

int main() {
    printf("The test_var is: %d\n", ++test_var); // prints 1
    use_it(); // prints 2
}

and test2.c

#include <stdio.h>
#include "test2.h"

int test_var;

void use_it() {
    printf("The test_var is: %d", ++test_var);
}

I replaced the definition of test_var with extern int test_var and got the same result. That is, in both cases both files, test.c and test2.c have access to the global variable test_var. I was under the impression that without extern, each file would have their own copy of test_var. Observation suggests that this is not the case. So when does extern actually do something?

Upvotes: 1

Views: 137

Answers (5)

Chris Dodd
Chris Dodd

Reputation: 126203

This is undefined behavior, as others have noted, but what you are seeing here is the common extension described in appendix J.5.11 of the C99 spec, where multiple external definitions in different compilation units are allowed as long as none or only one of them are initialized and the types of all of them are the same.

In this case, with the extension, the definitions will be combined into a single definition at link time.

You also appear to be confused by the fact that the extern keyword, when used at the global scope, has nothing to do with extern linkage for declarations and definitions. ALL declarations at the global scope have extern linkage unless they have a static or inline keyword. The extern keyword serves to make such a declaration just a declaration. Without the extern keyword a global variable declaration is also a definition, and that is the only effect of the extern keyword in the global scope.

Upvotes: 4

sir psycho sexy
sir psycho sexy

Reputation: 800

If you have the same variable declared in 2 diferent files as int test_var for example:

file1.c

int test_var;

file2.c

int test_var;

both variables will have their own memory adress, so they are two diferent variables with the same name.

if you have, two variables declared in 2 diferent files declared as extern int test_var, for example:

file1.c

extern int test_var; //this is a mistake

file2.c

extern int test_var; //this is a mistake

the compiler will return an error when you try to do something with that variables because with the keyword externyou are not reserving any space for that variable, you only use that keyword to say that a variable is already defined (commonly in another file).

The point is to unsderstand that a global variable is defined once with a sentence like int test_var (when you define a variable the compiler reserve space for it) and it's declared in every other file that need access to it with extern int test_var (when you declare a variable with the extern keyword you saying the compiler that variable is already defined and you want to have access to it in the file you are declaring it).

So an example of how to use a global variable wil be:

file1.c

int test_var; //definition

void useit(void);

int main () {
    test_var=7;
    useit();
    return 0;
}

file2.c

#include <stdio.h>
void useit (void) {

    extern int test_var; //declaration
    printf ("the variable value is %d",test_var);
}

Upvotes: 2

Andreas Bombe
Andreas Bombe

Reputation: 2470

If you are using gcc and possibly some other compilers, you just stumbled upon some Unix tradition. Namely that uninitialized global variables are placed in the common block where multiple definitions of the same variable are merged during linking.

gcc can be told to put uninitialized global variables into the data section with the option -fno-common. With this, the linker will report an error when there are multiple definitions of the same variable name.

Upvotes: 0

M.M
M.M

Reputation: 141554

To answer your question:

extern int test_var; is a declaration. This announces that "Somewhere, there should exist test_var . We don't know where that is yet, but by the time we finish compiling and linking, we will find it in exactly one place".

So there has to be exactly one definition to match. A definition serves as a declaration, and also says "Here is the storage for test_var".

Also, test_var could either have internal linkage or external linkage. The default behaviour is external linkage. If you provide more than one definition for a variable of external linkage, it is undefined behaviour.

Internal linkage is indicated by including static in the declaration. You can have as many definitions as you want of a static variable (so long as only one per file has an initializer).

Summing up, we have:

extern int test_var;    // declaration, external linkage
static int test_var;    // declaration, definition, internal linkage
int test_var;           // declaration, definition, external linkage

Note: the last two cases are actually tentative definitions: this is a thing that C has but C++ doesn't; the way it works is that it behaves like a declaration at first; but then , for each unit, if there is no later definition then this actually serves as a definition.

So you can write in C:

int test_var;
// stuff
int test_var = 5;

Upvotes: 1

ouah
ouah

Reputation: 145829

You end up with two copies of test_var and this is undefined behavior.

(C99, 6.9p5) "If an identifier declared with external linkage is used in an expression (other than as part of the operandof a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one"

In your case the linker may be nice with you and merges the two symbols but this is still not portable and is undefined behavior. If you are using the GNU linker, you can use --warn-common to get the warning (and --fatal-warnings if you want an error).

To fix your issue, put the extern specifier in the declaration of test_var in the .h file and remove one of the definition of test_var (for example the one in test.c file).

Upvotes: 5

Related Questions