以下为测试环境:
gcc 4.3.2-1-1
GNU/Linux Debian 5.0
(刚开始使用csdn博客,没经验。由于中间调试时间较长,忘了备份,刚写完,赶紧发了。一看,没有;再看,还是没有。挥去愤怒,重写一篇。望读者以此为鉴,及时备份。:-)
1、extern与static
extern 对变量、函数声明;编译时可见,即告诉编译器:“老兄,虽然这个文件里,我没有定义,但在别处定义了,你得放过我”。而出于检查和使用的需要,没有定义是不能放行的。
函数和变量都默认为extern的,在链接时所有文件可见;更甚的是默认为不加static和extern即为定义,这也就带来的extern显性声明的必然性。这将在后面详细分析。
static,字面意思是静态限定符,用于三种场合可产生三种效果:
a、作用于局部变量,在函数的生存期其值具有连续性,如何理解,通俗但不准确的表达:被初始化一次,以后每次调用该函数时继续上次的结果。譬如:
1
#include
<stdio.h>
2
3
#define COUNT
3
4
static
stat_count = COUNT - 1
;
5
int
stat()
6
{
7
static
count = COUNT;
8
9
count --;
10
printf("the result in N.O.
%d
invacation :
%d
.
\n
"
, stat_count, count);
11
return
count;
12
}
13
int
main()
14
{
15
while
(stat())
16
stat_count --;
17
return
0
;
18
}
the result in N.O. 2 invacation : 2.
the result in N.O. 1 invacation : 1.
the result in N.O. 0 invacation : 0.
b、作用于全局变量,只在本文件作用,其相对的是extern。具体比较将在后面分析。
c、作用于函数,仅供本文件其它函数调用,函数和变量都默认为extern。不是本文重点,不作具体分析。
抽象点,具有三个作用:
隐藏全局可见性;
保持变量内容的持久;
默认初始化为
0
。
这个总结见:
史蒂芬的技术博客
之
static的作用
http://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html
2、extern全局变量
现代编译器都按文件编译,编译时各个文件独立检查语法错误。各个全局变量互相透明,可见域在本文件内。
于是即使多个文件有多个定义也不报错,只是链接的时候所有全局变量保存在一个符号表,进行符号的定位。因此,报错也发生在链接阶段。
注意到了吗?link error
也就是说,可见性是在链接时扩展的,编译阶段局限于本文件。
type id;
变量和函数默认是extern的,即所有文件可见。
但是默认的不仅是声明,更是定义。C语言规定,标识符可以有多个声明,但必须有且仅有一次定义。
声明为了让编译器知道有哪些符号,什么类型,以便后面遇到时已经具备一定判断的意识(譬如,类型检查,初始化检查,定义检查)。
因此,多个文件出现了默认的
type id;就是多重定义;如果本意是多个文件共用一个符号,这时需要用到extern显性指定为声明。
extern type id;仅仅是声明,需要另外定义,且是别的文件里的定义。
不妨看看编译器是如何限制的(可以只看红色标记部分
):
1)、
extern char *str_ptr;
str_ptr = "abcd\0";
gcc编译:
test.c:(.text+0x13): undefined
reference to `str_ptr'
2)、
extern char *str_ptr;
char *str_ptr = "abcd\0";
gcc编译:
test.c:13: error: declaration of ‘str_ptr’ with no linkage
follows extern declaration
test.c:12: error: previous declaration
of ‘str_ptr’ was here
3)、
extern
char *str_ptr = "abcd\0";
gcc编译:
test.c:12: error: ‘str_ptr’ has both ‘extern’ and initializer
4)、
char *str_ptr = "abcd\0";
extern char *str_ptr;
gcc编译:
test.c:13: error: extern declaration of ‘str_ptr’ follows declaration with no linkage
test.c:12: error: previous definition of ‘str_ptr’ was here
3、extern作用于指针
不妨作如下测试:
文件1
1
/*
2
* file:test_declaration.c
3
* purpose: declaration of array used by test_extern.c
4
* maninter:hilyhoo AT gmail.com
5
*/
6
#include
<stdio.h>
7
8
char
str_ptr[] = "abcd
\0
"
;
9
char
str_array[] = "abcd
\0
"
;
10
11
int
null()
12
{
13
1
;
14
return
0
;
15
}
文件2
1
/*
2
* file:test_extern.c
3
* input: none
4
* rerult:print 'c' and report a segment error
5
* purpose: test extern point with define array
6
* maninter:hilyhoo AT gmail.com
7
*/
8
#include
<stdio.h>
9
10
int
main()
11
{
12
extern
char
*str_ptr;
13
extern
char
str_array[];
14
15
printf("extern pointer resrult :
%c
\n
"
, str_array[2
]);
16
printf("extern array resrult :
%c
\n
"
, str_ptr[2
]);
17
18
return
0
;
19
}
gcc编译运行:
gcc -g test_extern.c test_decleration.c
./a.out
extern pointer resrult : c
段错误
gdb调试:
(gdb) b main
Breakpoint 1 at 0x80483c1: file test.c, line 15.
(gdb) p str_ptr
$1 = 0x64636261 <Address 0x64636261 out of bounds>
(gdb) p str_array
$2 = 0x804961a "abcd"
(gdb) r
Starting program: /home/hilyhoo/c/a.out
Breakpoint 1, main () at test.c:15
15 null();
(gdb) s
null () at dec.c:14
warning: Source file is more recent than executable.
14 }
(gdb) p str_ptr
$3 = "abcd\000"
(gdb) x str_ptr
0x8049614 <str_ptr>: 0x64636261
(gdb) s
Line number 15 out of range; dec.c has 14 lines.
(gdb) s
main () at test.c:17
17 printf("extern pointer resrult : %c\n", str_array[2]);
(gdb) x str_array
0x804961a <str_array>: 0x64636261
(gdb) s
extern pointer resrult : c
18 printf("extern array resrult : %c\n", str_ptr[2]);
(gdb) x str_ptr
0x8049614 <str_ptr>: 0x64636261
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x080483e8 in main () at test.c:18
18 printf("extern array resrult : %c\n", str_ptr[2]);
上例为证:
char
str_ptr[] = "abcd
\0
"
;
extern
char
*str_ptr;
分别在两个文件,编译时独立,链接时,按照声明使用:
定义:str_ptr,数组,地址
0x8049614,
内容"abcd\0",16进制0x61、
0x62、
0x63、
0x64
链接:
str_ptr,指针,地址
0x8049614,
内容long形整数,16进制
0x64636261
str_ptr[2],*(str_ptr + 2),即*(
0x64636263
),无权访问,即报断错误。
str_ptr
==>"abcd"
0x64
0x63
0x62
0x8049614==>
0x61
……
0x64
0x63
0x62
str_ptr + 2 ==>
0x63
*(str_ptr + 2)==>?
可以看出:
指针符号是地址的地址;
数组符号,对应整个数组;如果作为一维数组变量名,则为其第一个单元的地址。
1)
数组仅仅是一个符号,作为参数时,由于形参压栈时,将数组首地址压进,即以一个空间来存放首地址,就蜕变成指针了;定义多维数组时也会被编译器解释成指针。
否则,数组名是不占单独的空间的,在符号表中是一个符号,地址为数组首地址,内容为首个单元内容。
2)
定义指针时,分配一个空间(我们的体系为32位,4个字节),其内为指向的单元的地址。
3、总结
1)、extern如果显式声明,在当前文件中在不得定义,且必须在其它链接到的文件定义。
(这是在gcc编译器中,据称有的编译器不一样。比如,可以使用
extern type id = initialize;
因此不敢妄言,恐误导读者。)
2)、指针与数组的差别还是很大的,但一般情况数组会蜕变为指针使用,譬如:
id[offset],编译器会解释成*(id + offset),因此一般用来几乎感觉不到差别。
分享到:
相关推荐
作者在网络版CFAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等...
书中列出了C用户经常问的400多个经典问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等各个方面的主题,并分别给出了解答,而且结合代码示例阐明要点。 《你必须知道的495个C语言问题》结构...
到底指针是一种数组,还是数组是一种指针? 6.11 我看到一些“搞笑”的代码,包含5["abcdef"]这样的“表达式”。这为什么是合法的C语言表达式呢? 数组的指针 6.12 既然数组引用会退化为指针,如果array是数组...
10.7 指针数组和指向指针的指针 161 10.7.1 指针数组的概念 161 10.7.2 指向指针的指针 164 10.7.3 main 函数的参数 166 10.8 有关指针的数据类型和指针运算的小结 167 10.8.1 有关指针的数据类型的小结 167 10.8.2 ...
10.7 指针数组和指向指针的指针 161 10.7.1 指针数组的概念 161 10.7.2 指向指针的指针 164 10.7.3 main 函数的参数 166 10.8 有关指针的数据类型和指针运算的小结 167 10.8.1 有关指针的数据类型的小结 167 10.8.2 ...
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。 C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。 3、请简述以下两个for循环的优缺点(5分) for (i=0; i;...
在C++中,有两种给出注释的方法:一种是延用C语言方法,使用"/*"和"*/"括起注释文字。另一种方法是使用"//",从"//"开始,直到它所在行的行尾,所有字符都被作为注释处理。 2-8 什么叫做表达式?x = 5 + 7是一个...