`
hilyhoo
  • 浏览: 13689 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

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

阅读更多

以下为测试环境:

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

分享到:
评论

相关推荐

    C语言变长数组之剖析

    C语言变长数组是C99标准引入的一个新特性,使得在运行时可以确定数组长度成为可能,打破了传统C语言中数组长度必须在编译时固定的限制。然而,与C++等其他支持变长数组的现代编程语言相比,C语言中的变长数组使用受...

    C语言中extern用法

    原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a 声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。 解决方案:将声明改为 extern char a[]; 问题2: extern...

    免费下载:C语言难点分析整理.doc

    函数指针数组与返回数组指针的函数 这部分介绍了函数指针和数组指针的高级用法。 ### 38. 右左法则- 复杂指针解析 这部分探讨了复杂的指针声明,并提供了一种易于理解的解析方法。 ### 39. 回车和换行的区别 这...

    C语言难点及分析

    本文将深入探讨C语言中的几个核心知识点,包括变量的作用域和存储类别、函数、数组、字符串、指针、文件、链表等,旨在帮助读者更好地理解和应用C语言。 #### 变量作用域和存储类别 变量的作用域决定了变量可以在...

    C语言难点分析整理

    数组和指针在C语言中紧密相关,掌握它们对于处理复杂数据结构至关重要。 - **数组**:数组是一系列相同类型的元素的集合,每个元素可以通过索引访问。多维数组可以视为嵌套的一维数组。 - **指针**:指针存储变量的...

    高级C语言 C 语言编程要点

    此外,还包括数组动态增长、宏定义技巧、运算符优先级、do/while(0)的用法、`exit()`与`return()`的区别、`extern`与`static`的作用、代码质量工具如PC-Lint的使用、字符串函数的应用、函数指针的复杂用法、链表和树...

    嵌入式Linux C语言面试试题1.pdf

    知识点:在C语言中,指针数组可以使用指针赋值或数组初始化来初始化。例如:int *p[10]; p[0] = &x; 或 int *p[] = {&x, &y, &z}; 4. 如何定义一个有10个元素的整数型指针数组? 知识点:在C语言中,可以使用以下...

    c语言指针问题

    而`c[0] = 'b'`是允许的,因为它作用于栈上的数组`c`。 10. **二维字符数组声明**: - **`char a[][10] = {"abcd", "aaaa"};`**:这是正确的,使用可变大小数组(VLA)声明二维字符数组。 - **`char b[2][] = {...

    c语言难点分析整理.pdf

    C语言是一种底层编程语言,广泛应用于系统开发、嵌入式编程等领域。对于初学者来说,理解和掌握C语言的难点是至关重要的。以下是对C语言中一些关键知识点的详细解析: 1. 变量的作用域和存储类别: - **作用域**:...

    (完整word版)C语言数组与函数阶段测试题答案-.doc

    C语言中的数组和函数是编程基础中的重要概念,它们在编写程序时起着至关重要的作用。数组是一种数据结构,允许存储相同类型的数据集合。而函数则是将一组操作封装起来,以便在程序的不同位置重复使用。 1. **数组**...

    C语言2复习题C语言2复习题.doc

    5. **数组与指针**:数组名在C语言中被视为指向数组首元素的指针常量,因此可以传递数组名给函数,函数会得到数组的首地址。数组元素可以通过索引访问,如`a[i]`等价于`*(a+i)`。数组定义如`int a[10]`表示包含10个...

    c语言PPT教程【c语言的教学】

    【C语言PPT教程——为初学者开启编程之旅】 C语言是一种基础且强大的编程语言,被广泛应用于系统编程、嵌入式系统、游戏开发、软件工程等多个领域。它以其高效、灵活和可移植性著称,是许多程序员的首选语言。本...

    C语言难点,C语言编程提升

    - 在传递数组作为函数参数时,通常传递数组首地址,因为数组名在大多数情况下等价于指向数组首元素的指针。 4. 指针: - 指针是C语言的精髓,它存储内存地址,允许直接访问和修改变量。 - 指针与数组紧密相关,...

    你必须知道的495个C语言问题.pdf

    6. 复杂声明的理解和建立:C语言允许创建非常复杂的声明,如函数指针数组。理解这些声明的正确方式对于深入C语言编程非常重要。 7. 对齐和填充:为了优化性能和兼容性,编译器可能会在结构体中加入填充字节,导致...

    你必须知道的495个C语言问题

    到底指针是一种数组,还是数组是一种指针? 6.11 我看到一些“搞笑”的代码,包含5["abcdef"]这样的“表达式”。这为什么是合法的C语言表达式呢? 数组的指针 6.12 既然数组引用会退化为指针,如果array是数组...

    C语言难点分析整理!

    理解函数是学习C语言的关键之一。函数不仅可以帮助我们组织代码,还能实现模块化编程。 - **函数的定义**:包括返回类型、函数名、参数列表。 - **函数的调用**:需要提供实际参数传递给函数的形式参数。 - **函数...

    C语言考试试题.pdf

    7. 指针的概念和使用:指针是C语言的核心概念之一,包括指针的声明、初始化、指针运算和指针与数组的关系。 8. 预处理指令的使用:如宏定义(#define)等。 9. 结构体的使用:结构体(struct)用于创建复杂数据...

    一个有用的C语言方面的文章

    例如,折半查找适用于已排序的数组。 - **数组作为函数参数**:传递数组时,实际上传递的是数组的指针。这意味着在函数内部修改数组会影响原始数组。 #### 字符串 - **字符串定义**:字符串在C语言中通常表示为...

    extern声明辨析

    在C语言中,数组类型和指针类型是不同的。`extern char *a;`声明了一个指向字符的指针,而不是一个字符数组。因此,这种声明方式与原始的数组定义不符,可能会导致运行时错误,例如非法内存访问。 - **正确做法**...

Global site tag (gtag.js) - Google Analytics