@ 有关static和extern关键字
@ MSDN
@ uselessstest
@ 2010.6.11
以下程序的编译,链接均采用VC6.0的编译器, 连接器.
以下面的程序为例
#include <stdio.h>
extern int i;
extern int j;
j = 1;
i = 1;
extern void f();
int main()
{
static int i;
int j = 10;
f();
printf("%d/n", i);
printf("%d/n", j);
return 0;
}
void f()
{
i++;
printf("%d/n", i);
}
输出结果是:
2
0
10
分析其运行过程.
extern int i; // 此处是声明了变量i, 并不是对static int i的extern.
extern int j; // 同样也是声明了变量j, 也不是对int j的extern.
j = 1; // 这是对变量j的赋值, 也就是定义.
i = 1; // 对变量i的赋值, 具体来说这里叫定义.
先来看上面的, 为什么一定要在这里赋值.
因为extern int i; extern int j只是声明了i和j, 声明是不开辟存储空间的, 只有定义才会.
因此下面必须要有定义.
下面这个程序是错误的.
extern int i;
void main()
{
i = 10; // 全局变量的赋值必须在任何函数外面.
}
再一点是, 赋值的时候必须是常量表达式. 如下的程序是错误的.
extern int i;
extern int j;
i = 10;
j = i; // j的初始化必须用常量表达式.
void main()
{
}
再接着看下面的语句.
extern void f(); // 让下面的f函数在这里可见, extern引用. 没什么好说的.
再就是main函数
第一句static int i; // 这句让外面的那个i在main函数里不可见了.
int j = 10; // 这句也让刚才在外面定义的j在main函数里不可见了.
接着调用f函数.
f函数里的第一个语句
i++; // 这里的i应该是i=1; 所定义的i, 于是这里i的值变为2
printf("%d/n", i); // 打印出的值也变为2.
于是回到main函数.
接着打印i, j, 这里的i和j就是static int i, 和 int j = 10; 里定义的i和j了.
.
对上面的程序做如下改变.
将f函数放到另外一个文件里, 命名为f.c
------------------- file test.c -----------------
#include <stdio.h>
extern int i;
extern int j;
j = 1;
i = 1;
extern void f();
int main()
{
static int i;
int j = 10;
f();
printf("%d/n", i);
printf("%d/n", j);
return 0;
}
------------- file f.c ------------------
#include <stdio.h>
extern int i; // 引用test.c文件里的i
void f()
{
i++;
printf("%d/n", i);
}
这样编译运行后, 依然是上面的那个结果.
下面对f.c里的f函数做点改变.
改为static void f();
接着编译运行, 发现不能通过.
当时如果把static void f()函数放到test.c文件里, 然后编译运行就一切正确.
用static定义的函数, 只能在一个文件里面对其他函数可见, 在其他文件里就是用了extern也不可见.
接着来分析一下相应程序的汇编代码
---------------------- test.c -------------------------
#include <stdio.h>
extern void f();
int main()
{
f();
return 0;
}
static void f()
{
printf("this is function f/n");
}
(要想看到汇编码, 只要在编译的时候添加编译器选项/FAcs即可.
vc6里面的设置是: 工程->设置->c/c++-> 分类里选择"文件列表"->列表文件类型选择
"Assembly, Machine Code, and Source"即可)
------------ 汇编码大致如下(仅复制出了关键部分) ------------
;如果将static关键字去掉, 那得到的汇编码里, 在PUBLIC _main前还有个PUBLIC _f
PUBLIC _main
_TEXT SEGMENT
_main PROC NEAR
;{
push ebp
mov ebp, esp
; f();
call _f
; return 0;
xor eax, eax
:}
pop ebp
ret 0
_main ENDP
_TEXT ENDS
EXTRN _printf:NEAR
_DATA SEGMENT
$SG338 DB 'this is function f', 0ah, 00h
_DATA ENDS
_TEXT SEGMENT
_f PROC NEAR
;{
push ebp
mov ebp, esp
; printf("this is function f/n");
push OFFSET FLAT:$SG338
call _printf
all esp, 4
;}
pop ebp
ret 0
_f ENDP
_TEXT ENDS
END
因此关键就在于那个PUBLIC.
如果你在另外一个文件里面定义static void f(), 那在该文件里没有PUBLIC, 因此EXTERN也没用.
那在同一个文件里的时候, 看上面的汇编码, 它也没有PUBLIC _f, 而且f函数定义在main函数后面,
那main函数怎么依旧能调用f函数?
对于这点我目前不大确定, 根据我的汇编经验, 汇编码里面应该是可以看见的.
就像你用jum指令可以跳到后面的标号去, 而不用在前面声明标号.
接下来的问题就是, 既然可以看见, 那为什么还要用extern void f();
这就是c语言里面的规定了.
如果不用extern void f() (该语句可以就用void f(); 或 static void f();来代替)
那程序如下:
#include <stdio.h>
int main()
{
f(); // 当C编译器检查到这里的时候, 会默认f()为一个声明,返回值默认的添加为int
// 即这条语句在c编译器看来和语句 int f(); 等价. 相当于声明了一个返回int的函数.
// 因此前面必须加extern void f(); 来声明f, 这样在这里就表示对f函数的调用了.
return 0;
}
static void f()
{
printf("this is function f/n");
}
总结下:
1: extern只能引用外部变量和函数, 外部变量就是定义在所有函数外的变量, 即全局变量.
因为函数不能嵌套, 所以函数永远就是外部的.
2: static声明的函数能将该函数作用域限制在一个文件里. 对于变量也是这样的.
3: 全局变量的定义必须在所有函数外面, 定义时只能用常量表达式赋值.
下面对2种情况进行比较.
(1)
文件 test.c
#include <stdio.h>
int main()
{
f();
return 0;
}
void f()
{
printf("this is function f/n");
}
编译后又一条警告, 一条错误.
test.c(5) : warning C4013: 'f' undefined; assuming extern returning int
test.c(10) : error C2371: 'f' : redefinition; different basic types
(2)
文件 test.c
#include <stdio.h>
int main()
{
f();
return 0;
}
文件 f.c
#include <stdio.h>
void f()
{
printf("this is function f/n");
}
编译后仅有一条警告, 如果运行的话, 程序运行正确.
test.c(5) : warning C4013: 'f' undefined; assuming extern returning int
上面的内容如有错误, 还望指出, 多谢!
晕, CSDN这里面怎么对TAB识别不出来. 还是说tab不能复制过来.