extern
在C语言中,一个文件中声明的全局变量和函数是默认以"extern"声明的,是外部文件可见的.
int x = 1;
int foo(int var)
{
return var + x;
}
// 等价于
extern int x = 1; // 但是这句产生编译器警告,和"extern int x;"引用外部的x非常容易混淆.
extern int foo(int var)
{
return var + x;
}
一个文件中以"static"声明的变量和函数是外部文件不可见的.
static int x = 1; // 仅仅是本文件范围的变量
static int foo(int var) // 仅仅是本文件范围的函数
{
return var + x;
}
由于"static"是对于外部范围不可见的,所以可以用"static"声明和外部函数同名的函数,但不要在同一范围使用.
static int x = 1;
int bar()
{
static int x = 2;
{
prinf("%d", x);
static int x = 3;
prinf("%d", x);
}
return x;
}
上述的三个静态x都会被分配内存,编译器会使用三个不同的标号:
x:
.long 1
x.1234:
.long 2
x.1235:
.long 3
bar:
...
movl x.1234(%rip), %esi
...
call printf
...
movl x.1235(%rip), %esi
...
movl x.1234(%rip), %eax
虽然三个x在汇编中没有任何的区别,但是C编译器是按照源文件的逻辑来使用变量的:1.先在所在的周期范围查找;2.再在更大的周期范围一步一步向上查找.所以第一个printf打印2,第二个printf打印3,返回值为2.
从外部引用变量和函数.
tool.c
float var = 1000.11;
int func(int a, int b)
{
return a + b;
}
=================================================
main.c
extern float var; // 不要是"extern int big_num = 1",这样是声明不是引用.
extern int func(int, int);
float main ()
{
func(1, 2);
return var;
}
如果没有"extern int var;".
extern int func(int, int);
float main ()
{
func(1, 2);
return var;
}
凭空岀现的var无法通过C编译器.
如果没有"extern int func(int, int);".
extern float var;
float main ()
{
func(1, 2);
// 或者 int a = func(1, 2);都一样
return var;
}
是可以通过C编译器的,因为"func(1, 2);"不需要函数原型就可以编译成:
movl $2, %esi
movl $1, %edi
call func
所以通过C编译器是应该的.但是没有了函数原型的限制func函数多次使用写错了也是可以通过C编译器的.
extern float var;
float main ()
{
func(1, 2);
func(1);
func();
func(1, 2, 3, 4);
return var;
}
因为C编译器根本不知道func的原型是不是可变参数.
邪门歪道
交换了var和func能不能通过C编译和汇编?能,因为在链接之前两个文件是不见面的,自己过自己的.能不能通过链接?
tool.c
float var = 1000.11;
int func(int a, int b)
{
return a + b;
}
=================================================
main.c
extern float func;
extern int var(int, int);
float main ()
{
var(1, 2);
return func;
}
能,因为main.c通过C编译器的得到的汇编文件中只是用func和var符号完成指令:
movl $2, %esi
movl $1, %edi
call var
mov func(%rip), %xmm0
但是不会分配内存.这样在通过汇编器得到可重定向文件时,使用var和func的地方被写入.rel .data和.rel .text重定向表, 在.symtab符号表中var和func被标记为NOTYPE,因为汇编文件中并没有var和func的主体,只是平平无奇的两个内存地址.
链接器是纯洁的,只做重定向,从不怀疑.链接tool.o和main.o时,查找tool.o符号表中符号名称为var和func符号对应的内存地址去重定向main.o中的var和func,使得main.c中的var函数是tool.c中var变量的地址,func变量是tool.c中func函数的地址.
总结:
extern用来声明定义外部可见的变量或函数,在汇编文件中用.global伪指令标记,在可重定向文件的符号表中用global标记;static用来声明定义外部不可见的变量或函数,在汇编文件中用.local伪指令标记,在可重定向文件的符号表中用local标记;
extern用来引用外部的变量或函数,在汇编文件中只使用变量或函数的符号,但不给符号分配任何内存,在可重定向文件中和符号有关的位置都被写入重定向表.