在 C 代码的编译或链接过程中的什么时候隐式定义了外部变量?
At what point during compilation or linking of C code are extern variables implicitly defined?
如果我的项目在同一目录下有以下3个文件:
mylib.h:
int some_global;
void set_some_global(int value);
mylib.c:
#include "mylib.h"
void set_some_global(int value)
{
some_global = value;
}
main.c:
#include <stdio.h>
#include "mylib.h"
int main()
{
set_some_global(42);
printf("Some global: %d\n", some_global);
return 0;
}
我用
编译
gcc main.c mylib.c -o prog -Wall -Wpedantic
我没有收到任何错误或警告,prog
程序将 42
打印到控制台。
当我第一次尝试这个时,我预计会出现“多重定义”错误或某种警告,因为 some_global
未在头文件中声明 extern
。在研究这个问题后,我发现在 C 语言中 extern
隐含在函数外的变量声明中(并且 C++ 的情况恰恰相反,这可以通过使用 g++
而不是 [= 来证明) 20=] 在上面的编译行中)。此外,如果我将 mylib.h
中的行从声明更改为定义(例如 int some_global = 1;
),我 do 会得到我预期的“多重定义”错误(这并不令人震惊)。
我的主要问题是:在哪里定义变量?它似乎在某处某处隐式定义,但编译器或链接器在什么时候意识到它需要定义该变量并这样做了?
此外,如果我在 mylib.h
文件中将变量显式声明为 extern
,为什么会出现“未定义引用”错误,除非我在一个且唯一的一个文件中显式声明该变量*.c
?我希望考虑到上面代码工作的原因(extern
是隐式的),明确声明 extern
不会有什么不同。为什么行为会有所不同?
跟进
在下面的答案更正我 mylib.h
中的代码是“暂定定义”而不是声明后,我发现了这个相关答案,其中包含有关此类问题的更多详细信息:
你的代码编译和 links 没有错误只是因为你使用 gcc
是用 -fcommon
命令行选项编译的 ” -fcommon 将未初始化的全局变量放在公共块中。这允许 linker 将不同编译单元中同一变量的所有暂定定义解析为同一对象,或非暂定定义。(... ) 它主要用于启用遗留代码 link 而不会出错。" 这是版本 10 之前的默认设置,但即使现在许多工具链仍在构建时启用此选项。
永远不要在头文件中定义数据。在头文件中仅放置 extern
变量定义。
应该是:
extern int some_global;
void set_some_global(int value);
mylib.c:
#include "mylib.h"
int some_global;
void set_some_global(int value)
{
some_global = value;
}
main.c:
#include <stdio.h>
#include "mylib.h"
int main()
{
set_some_global(42);
printf("Some global: %d\n", some_global);
return 0;
}
int some_global;
为暂定定义。在版本 10 之前的 GCC 中,GCC 生成了一个目标文件,将其视为一个公共符号。 (此行为仍然可以通过开关选择,-fcommon
。)链接器将一个公共符号的多个定义合并为一个定义。
如果我的项目在同一目录下有以下3个文件:
mylib.h:
int some_global;
void set_some_global(int value);
mylib.c:
#include "mylib.h"
void set_some_global(int value)
{
some_global = value;
}
main.c:
#include <stdio.h>
#include "mylib.h"
int main()
{
set_some_global(42);
printf("Some global: %d\n", some_global);
return 0;
}
我用
编译gcc main.c mylib.c -o prog -Wall -Wpedantic
我没有收到任何错误或警告,prog
程序将 42
打印到控制台。
当我第一次尝试这个时,我预计会出现“多重定义”错误或某种警告,因为 some_global
未在头文件中声明 extern
。在研究这个问题后,我发现在 C 语言中 extern
隐含在函数外的变量声明中(并且 C++ 的情况恰恰相反,这可以通过使用 g++
而不是 [= 来证明) 20=] 在上面的编译行中)。此外,如果我将 mylib.h
中的行从声明更改为定义(例如 int some_global = 1;
),我 do 会得到我预期的“多重定义”错误(这并不令人震惊)。
我的主要问题是:在哪里定义变量?它似乎在某处某处隐式定义,但编译器或链接器在什么时候意识到它需要定义该变量并这样做了?
此外,如果我在 mylib.h
文件中将变量显式声明为 extern
,为什么会出现“未定义引用”错误,除非我在一个且唯一的一个文件中显式声明该变量*.c
?我希望考虑到上面代码工作的原因(extern
是隐式的),明确声明 extern
不会有什么不同。为什么行为会有所不同?
跟进
在下面的答案更正我 mylib.h
中的代码是“暂定定义”而不是声明后,我发现了这个相关答案,其中包含有关此类问题的更多详细信息:
你的代码编译和 links 没有错误只是因为你使用
gcc
是用-fcommon
命令行选项编译的 ” -fcommon 将未初始化的全局变量放在公共块中。这允许 linker 将不同编译单元中同一变量的所有暂定定义解析为同一对象,或非暂定定义。(... ) 它主要用于启用遗留代码 link 而不会出错。" 这是版本 10 之前的默认设置,但即使现在许多工具链仍在构建时启用此选项。永远不要在头文件中定义数据。在头文件中仅放置
extern
变量定义。
应该是:
extern int some_global;
void set_some_global(int value);
mylib.c:
#include "mylib.h"
int some_global;
void set_some_global(int value)
{
some_global = value;
}
main.c:
#include <stdio.h>
#include "mylib.h"
int main()
{
set_some_global(42);
printf("Some global: %d\n", some_global);
return 0;
}
int some_global;
为暂定定义。在版本 10 之前的 GCC 中,GCC 生成了一个目标文件,将其视为一个公共符号。 (此行为仍然可以通过开关选择,-fcommon
。)链接器将一个公共符号的多个定义合并为一个定义。