我无法理解该程序的奇怪行为。它会发出警告,但输出结果令人惊讶。我有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时,它是如何实现的。
答案 0 :(得分:3)
您似乎有未定义的行为 - 但在很多时候很多机器上可能都是无害的。
由于您的extern
声明var
是double
,当它实际上是int
时,尝试以双精度访问它会导致未定义的行为。
然而,实际上,在典型情况下,double
将是8个字节,其中int
是4个字节。这意味着当您将var
传递给printf时,8个字节的数据将被压入堆栈(但从var
实际占用的四个字节开始。)
printf
将尝试将其前四个字节(对应于var
的字节)转换为int
,它与{{1}的实际类型匹配1}}。这就是为什么这种行为常常是无害的。
同时,如果您的编译器正在进行边界检查,那么尝试加载8字节的四字节变量很容易导致某种“越界”异常,因此您的程序将无法正常工作。这只是你的不幸,大多数C编译器都没有进行任何边界检查。
也可能(可能在大端机器上)而不是与var
对应的字节发生在堆栈的正确位置,var
将printf
作为{int
访问它们1}},构成double
的其他四个字节将最终出现在那个地方,并且会打印垃圾。
另一种可能性是,8字节的位模式恰好对应于某个浮点值,一旦您尝试将其作为浮点数访问,就会导致异常。鉴于(小)百分比的位模式对应于信号NaN的情况,但这种情况的可能性相当小(即使正确的位模式也可能不会触发任何东西,如果编译器只是在堆栈上推了8个字节并离开它在那)。
答案 1 :(得分:2)
仅在extern
定义上设置类型不会更改存储在内存中的值 - 它只会更改值的解释方式。因此,0x0001
的值是存储在内存中的内容,无论如何尝试解释它。
调用func()后,您已将值0x0001
存储在a
存储的位置。然后在main
中a
传递给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
(虽然这取决于编译器如何存储变量,但它很可能会将它们存储在相邻位置)