tanascius
tanascius

Reputation: 53944

C: Initialize module variable

I got two modules (compile units), both using a module variable with the same name:

FileA.c and
FileB.c both contain:

#includes

int m_Test;

// Functions

That's no problem, both variables are independent, as expected - but as soon as I assign values to the variables like:

int m_Test = 0;

I get (using VS2008) the error LNK2005: m_Test already defined in ...

So I probably have a problem to understand what I am doing :) What is happening, when I try to initialize a module variable like here? I could not find information about it (google, newsgroup faq, SO).

Thanks!

Upvotes: 1

Views: 5495

Answers (5)

AnT stands with Russia
AnT stands with Russia

Reputation: 320631

When you declare your variable as in your original post, the variable gets external linkage (with our without initializer - doesn't matter). This means that the variables are not independent. In both declarations the names refer to the same object. In fact, what you have is a multiple definitions of the same entity with external linkage. This is a violation of C language definition rules, i.e. it is what we usually call an error. Note, that this is an error in any case: with or without initializer.

The reason your code seems to compile fine without an initializer is just a quirk of your compiler.

I can actually guess the rationale behind that quirk. You see, C language has an interesting feature (not present in C++, BTW) called tentative definitions. This feature says that if you declare a variable like that, without initializer

int m_Test; /* no initializer */

you create a tentative definition of that variable. You can declare it in the same way multiple times in the same translation unit

int m_Test; 
int m_Test; /* OK in C */
int m_Test; /* OK in C */

(note, again, that this would be illegal in C++). If at some point you provide a non-tentative definition for that variable

int m_Test = 0; /* non-tentative */

the compiler will "merge" all these previous tentative definitions with this one. But if you don't provide a non-tentative definition, the compiler will generate one for you implicitly, and it will be equivalent to

int m_Test = 0; /* implicit definition generated by the compiler */

Note, however, that this only applies within the same translation unit. That means that once you wrote something like that

int m_Test; /* tentative */

it already guarantees that this variable will eventually be non-tentatively defined in this translation unit (by you explicitly, or by the compiler implicitly). If you do the same thing in another translation unit, that variable will be defined there as well, which will violate the one definition rules of C language.

However, the compiler you are using must be treating the tentative definition rules more losely and allows something like "tentative definitions across several translation units". This is why you don't get an error in your first case. However, this is just a quirk of your compiler. It is still illegal in C.

P.S. As it has been noted in one of the linked posts, this behavior is a non-portable "common extension" of C compilers (even mentioned in the informative section of the language standard). I.e. some compilers are known to allow multiple declarations of external objects as long as no more than one of them includes an initializer.

P.P.S. Of course, if you want to have indepedent variables in different translation units, you have to declare them as static, as others already noted.

Upvotes: 7

ChrisW
ChrisW

Reputation: 56123

Instead of defining it in two *.c files, you should use:

extern int m_Test;

Use this in every *.c file except one, or, preferably, put this extern declaration into a header file (which you can then include into any number of *.c files).

Only one file should contain the non-extern definition:

int m_Test = 0;

The above is good advice, if (only if) you want the same variable to used in all *.c files: so that, for example, if you change its value in one file, then the changed value is visible when you read it from the same variable in another file.

If, alternatively, you want several independent instances of the variable (where each *.c file has its own independent copy of the variable), then define each of them as a 'static' variable, e.g. define the following in each of the several *.c files:

static int m_Test = 0;

The static approach is working, Can you explain what happens when I add = 0 to the definition, maybe?

Adding = 0 to the definition would be harmless: it's permitted to initialize a variable when you define it, and when you do initialize a static variable the variable is still static (i.e. still has 'internal linkage' and isn't visible in other modules).

It's a slightly useless thing to do, because you can instead assume that the initial value of a static int variable is guaranteed to be zero (unless some other value is specified using an initializer); but it's quite possibly a good idea to specify it anyway, if only to document what the initial value is (instead of depending on a language rule that you're not very familiar with).

Upvotes: 3

Christoph
Christoph

Reputation: 169683

File-scoped variables are extern by default; see C99 spec, 6.2.2 §5:

If the declaration of an identifier for a function has no storage-class specifier, its linkage is determined exactly as if it were declared with the storage-class specifier extern. If the declaration of an identifier for an object has file scope and no storage-class specifier, its linkage is external.

If you want your variables to be distinct, add the specifier static.

Upvotes: 1

abelenky
abelenky

Reputation: 64710

If you want the variables to be independent, declare them static.

static int m_Test;

Means that the variable m_Test is only visible in that file-scope, and is not available to other files or modules.

Upvotes: 5

Daniel Bingham
Daniel Bingham

Reputation: 12914

Are you accidentially including an external definition of the variable? IE including something like this:

extern int m_Test; 

From FileA in FileB or vica versa?

Check your header files and make sure none of them contain something like that.

Upvotes: 1

Related Questions