如果您删除 stdio.h 您的输出可能会更有意义。让我们忽略那个库,因为它包含内部变量。
在您的具体情况下,会发生以下情况:
int uninit_global_var;
由于这是在文件范围内分配的变量,因此具有静态存储持续时间,就像任何声明为static
. C 标准要求,如果具有静态存储持续时间的变量没有被程序员显式初始化,在这种情况下,必须在程序启动之前将其设置为零。所有这些变量都放在.bss
段中。
int init_global_var=5;
此变量也在文件范围内分配,因此它也将具有静态存储持续时间。但在这种情况下,它是由程序员初始化的。C 标准要求在程序启动之前将这些变量设置为给定的值。这些变量被放置在.data
段中。
int local_var;
此变量具有自动存储持续时间(本地)。编译器很可能会优化掉这个变量,因为它没有任何意义。但是让我们假设这种优化不会发生。然后,该变量将在运行时分配,当它所在的范围(块)被执行时,一旦该范围结束(它超出范围)就不再存在。它将被分配在堆栈上或 CPU 寄存器中。换句话说,在链接时,这个变量只作为程序代码存在,以一些汇编指令的形式存在,说“将一个 int 压入堆栈”,然后“从堆栈中弹出一个 int”。
如何初始化这些不同类型的变量取决于系统。但通常在调用 main 之前编译器会注入一些代码。这是一种过度简化,但出于教学的原因,您可以想象您的程序实际上看起来像这样:
bss
{
int uninit_global_var;
}
data
{
int init_global_var;
}
rodata
{
5;
}
int start_of_program (void) // called by OS
{
memset(bss, 0, bss_size);
memcpy(data, rodata, data_size);
return main();
}
数据:4 bss:4
具有真正非易失性存储器的嵌入式系统将与上述代码完全相同,而基于 RAM 的系统可能会以不同的方式解决数据初始化部分。bss 在所有系统上的工作方式都相同。
您可以通过运行以下程序轻松验证它们是否存储在不同的段中:
char uninit1;
char uninit2;
char init1 = 1;
char init2 = 2;
int main (void)
{
char local1 = 1;
char local2 = 2;
printf("bss\t%p\t%p\n", &uninit1, &uninit2);
printf("data\t%p\t%p\n", &init1, &init2);
printf("auto\t%p\t%p\n", &local1, &local2);
}
您将看到“uninit”变量被分配在相邻的地址,但在与其他变量不同的地址。与“init”变量相同。“本地”变量可以分配到任何地方,因此您可以从这两个变量中获得任何类型的奇怪地址。