外部和全局变量

时间:2012-04-01 05:12:34

标签: c

我无法理解该程序的奇怪行为。它会发出警告,但输出结果令人惊讶。我有3个文件,file1.c,file2c,file1.h

file1.c:

#include "file1.h"

extern void func(void);

 int main(void)
{
func();
printf("%d\n",var);
return 0;
}

file2.c中:

int var;

 void func(void)
{
var = 1;
}

和file1.h是:

#include<stdio.h>

extern double var;

当我编译并运行时,它会按预期显示警告,但是在打印时会显示1.但是当我更改file2.c中声明的var时,它是如何实现的。

2 个答案:

答案 0 :(得分:3)

您似乎有未定义的行为 - 但在很多时候很多机器上可能都是无害的。

由于您的extern声明vardouble,当它实际上是int时,尝试以双精度访问它会导致未定义的行为。

然而,实际上,在典型情况下,double将是8个字节,其中int是4个字节。这意味着当您将var传递给printf时,8个字节的数据将被压入堆栈(但从var实际占用的四个字节开始。)

然后

printf将尝试将其前四个字节(对应于var的字节)转换为int,它与{{1}的实际类型匹配1}}。这就是为什么这种行为常常是无害的。

同时,如果您的编译器正在进行边界检查,那么尝试加载8字节的四字节变量很容易导致某种“越界”异常,因此您的程序将无法正常工作。这只是你的不幸,大多数C编译器都没有进行任何边界检查。

也可能(可能在大端机器上)而不是与var对应的字节发生在堆栈的正确位置,varprintf作为{int访问它们1}},构成double的其他四个字节将最终出现在那个地方,并且会打印垃圾。

另一种可能性是,8字节的位模式恰好对应于某个浮点值,一旦您尝试将其作为浮点数访问,就会导致异常。鉴于(小)百分比的位模式对应于信号NaN的情况,但这种情况的可能性相当小(即使正确的位模式也可能不会触发任何东西,如果编译器只是在堆栈上推了8个字节并离开它在那)。

答案 1 :(得分:2)

仅在extern定义上设置类型不会更改存储在内存中的值 - 它只会更改值的解释方式。因此,0x0001的值是存储在内存中的内容,无论如何尝试解释它。

调用func()后,您已将值0x0001存储在a存储的位置。然后在maina传递给printf时,存储在位置a的值加上接下来的4个字节(假设为32位)被压入堆栈。也就是将值0x0001xxxx推入堆栈。但%d将传入的值视为int,并将读取堆栈中的前4个字节,即0x0001,因此它将打印1。

尝试更改代码,如下所示:

file1.c:

#include "file1.h"

extern void func(void);

int main(void)
{
    func();
    printf("%d %d\n",var);
    return 0;
}

file2.c中:

int var;
int var2;

void func(void)
{
    var = 1;
    var2 = 2;
}

和file1.h是:

#include<stdio.h>

extern double var;

你可能会得到输出1 2(虽然这取决于编译器如何存储变量,但它很可能会将它们存储在相邻位置)