C语言之extern作用于指针、数组

来源:互联网 发布:淘宝云栈菜鸟打印组件 编辑:程序博客网 时间:2024/06/05 19:28

以下为测试环境:

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 withno 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),因此一般用来几乎感觉不到差别。

原创粉丝点击