我有两个模块(编译单元),都使用具有相同名称的模块变量:
FileA.c和
FileB.c都包含:
#includes int m_Test; // Functions
这没有问题,两个变量都是独立的,如预期的那样 - 但只要我为变量分配值,如:
int m_Test = 0;
我得到(使用VS2008)错误 LNK2005: m_Test already defined in ...
所以我可能有一个问题要理解我在做什么:)当我尝试初始化像这里的模块变量时发生了什么?我找不到有关它的信息(google,newsgroup faq,SO).
谢谢!
当您在原始帖子中声明变量时,变量将获得外部链接(使用我们的无初始化器 - 无关紧要).这意味着变量不是独立的.在两个声明中,名称引用相同的对象.实际上,您拥有的是具有外部链接的同一实体的多个定义.这违反了C语言定义规则,即我们通常称之为错误.请注意,这在任何情况下都是错误的:有或没有初始化程序.
你的代码在没有初始化程序的情况下编译好的原因只是编译器的一个怪癖.
我实际上可以猜出这个怪癖背后的基本原理.你看,C语言有一个有趣的特征(在C++,BTW中不存在),称为暂定定义.这个特性说如果你声明一个这样的变量,没有初始化器
int m_Test; /* no initializer */
您创建该变量的暂定定义.您可以在同一翻译单元中以相同的方式多次声明它
int m_Test; int m_Test; /* OK in C */ int m_Test; /* OK in C */
(再次注意,这在C++中是非法的).如果在某些时候,您为该变量提供了非暂定的定义
int m_Test = 0; /* non-tentative */
编译器将所有这些先前的暂定定义"合并"到这个定义.但是如果你没有提供非暂定的定义,编译器将隐式生成一个,它将等同于
int m_Test = 0; /* implicit definition generated by the compiler */
但请注意,这仅适用于同一翻译单元.这意味着,一旦你写了这样的东西
int m_Test; /* tentative */
它已经保证这个变量最终将在这个转换单元中非暂时定义(由您明确地或由隐式编译器).如果你在另一个翻译单元中做同样的事情,那么也会定义那个变量,这将违反C语言的一个定义规则.
但是,您使用的编译器必须更加丢失地处理暂定定义规则,并允许类似"跨多个翻译单元的暂定定义"之类的内容.这就是为什么在第一种情况下不会出现错误的原因.但是,这只是编译器的一个怪癖.它在C中仍然是非法的.
PS正如其中一个链接帖子所述,这种行为是C编译器的一个不可移植的"通用扩展"(甚至在语言标准的信息部分中提到过).即一些编译器已知允许外部对象的多个声明,只要其中不超过一个包含初始化器即可.
PPS当然,如果你想在不同的翻译单元中使用独立变量,你必须static
像其他人已经注意到的那样将它们声明为.
如果您希望变量是独立的,请声明它们static
.
static int m_Test;
表示变量m_Test仅在该文件范围中可见,并且不可用于其他文件或模块.