分析——代码有什么问题
在您的代码中:
文件1.c
int main(void)
{
char i; // Warning: unused variable
foo();
}
文件2.c
void foo(void)
{
extern int i;
i = 130;
printf("%d", i);
}
变量i
in与 in 所引用的变量main()
完全无关。变量in是严格本地的,不会传递给; 无法看到。i
foo()
i
main()
main()
foo()
foo()
i
在带有 GCC 4.7.1 的 Mac OS X 10.7.5 上,文件单独编译,但无法链接,因为没有 variable 的定义i
。
如果我们将变量移到i
之外main()
,则程序链接:
文件2.h
extern void foo(void);
文件1.c
#include <stdio.h>
#include "file2.h"
char i = 'A';
int main(void)
{
printf("i = %d (%c)\n", i, i);
foo();
printf("i = %d (%c)\n", i, i);
return(0);
}
文件2.c
#include "file2.h"
#include <stdio.h>
void foo(void)
{
extern int i;
i = 130;
printf("i = %d (%c)\n", i, i);
}
这现在链接并运行,但它调用未定义的行为。链接器没有注意到i
应该是sizeof(int)
但是的大小sizeof(char)
。但是,中的分配foo()
超出了i
分配的范围。它不会在该程序中造成明显的损坏,但这纯粹是运气。
当我在 little-endian 机器(Intel x86/64)上运行它时,输出为:
i = 65 (A)
i = 130 (?)
i = -126 (?)
当我在大端机器 (SPARC) 上运行它时,我得到了不同的结果(这并不奇怪;行为未定义,因此任何结果都是有效的):
i = 65 (A)
i = 130 (?)
i = 0 ()
4 字节整数的最高有效字节被写入 1 字节字符的唯一字节,因此第三行输出中的零。还要注意,幸运的是,该字符被分配了一个 4 字节的倍数的地址;这并不能保证,并且 an 的未对齐地址int
会导致总线错误(尽管实验表明它不会在 SPARC 上发生,即使i
强制使用奇数地址;我不确定发生了什么)。
合成——正确处理extern
处理这个问题的正确方法是避免extern int i;
在任何源文件中编写外部变量声明;此类声明应仅出现在需要使用变量的任何地方使用的标头中。
文件2.h
extern char i;
extern void foo(void);
有了该更改(并extern int i;
删除了相应的更改),则输出是自洽的:
i = 65 (A)
i = -126 (?)
i = -126 (?)
请注意标题在保持两者file1.c
并file2.c
相互一致方面的作用。它确保 in 中的定义与 infile1.c
中的声明相匹配file2.h
,并且 in 的使用file2.h
确保file2.c
在那里使用的声明也是正确的。另请参阅什么是extern
C 中的变量。