C语言中标识符的作用域、命名空间、链接属性、生命周期、存储类型(上)

Technorati 标签: C,标识符,作用域,命名空间,链接属性,生命周期,存储类型,scope,name space,linkage,storage durations,lifetime
 
        无论学习哪一种语言,都免不了要讨论这些问题。而且这些问题,深究起来有时也让我们很迷惑。
 
       标识符的定义无需多讲,只需注意不仅仅是指变量,还有函数,标签等。
 
1. 标识符的作用域
 
        作用域是指允许对标识符进行访问的位置范围。按照C99(章节6.2.1),C语言的作用域共有 4 种类型:文件作用域、代码块作用域、函数作用域、函数原型作用域。
 

 

类型
 

位置
 

说明
 


文件作用域  (file)
 
在所有 代码块和参数列表 之外
 
整个文件内都可以访问
 


代码块作用域 ( block)
 
在“代码块”或者“函数的参数列表”内部
 
只有所在的代码块内可以访问
 


函数作用域 (function)
 
函数体内
 
具有此作用域的只有一种语句:只有goto语句要使用的“语句标签”。简化为一条规则:一个函数中的语句标签(即label)不可相同。
 


函数原型作用域  (function prototype)
 
声明的函数原型的参数列表中(注意与“函数定义”不同)
 
由于函数原型的参数名称可以省略,即使不省略,也不要求和“函数定义”中的形参列表中名称相同。
只有一种情况会发生冲突:参数列表中的有重复的变量名。(这时编译报错: redefinition of parameter )
 

        说明:当出现两个标识符名称相同的情况,而且都属于同一个命名空间,那么在内层代码块,内层的那个标识符会隐藏外层的那个标识符。
 
举例说明并分析:
 


view plaincopy to clipboardprint?
01.int my_func(int a, int b);  /* myfunc是“文件作用域”;a,b是 “函数原型作用域” */  
02.int a;/* a是文件作用域。 注意:虽然上面的函数原型中将参数名称声明为a, 但是由于作用域不同,是合法的。下一行的b也是这种情况 */  
03.static int b; /* b是文件作用域 */  
04.int d( int n ){ /* d是“文件作用域”。因为这是函数定义,而不是函数原型,所以形式参数n 是“代码块作用域” */ 
05.                /* 由于形式参数中已经声明n,那么在函数体内的最外层变量的名称就不能再为n,因为同一个作用域内不允许对同一个变量进行多次声明。 
06.                      如果声明,编译器会提示重复声明变量。(在某些较老版本的编译器是允许的,但是C99标准是不允许的) 
07.                    在不同的作用域内可以 */ 
08.    int f;  /* f是代码块作用域 */ 
09.     
10.    int g(int k);   /* 函数原型,位于函数体代码块内。声明的函数名称g是“代码块作用域”,参数k是“函数原型作用域” */ 
11.   
12.  my_label:  /* 定义一个label,是“函数作用域” */      
13.    
14.    ...  /*  下面的代码块可以是while循环、for循环或if语言等等*/  
15.    {   
16.        int f, g, i; /* 都是代码块作用域,而且只是在内层代码块,在外层代码块不可见 */ 
17.                    /* 对于f,外层已经存在f,这里会隐藏掉外层的f,即在这个内层代码块中无法访问外层的f */ 
18.        int n;     /* 代码块作用域,由于这里已经不是函数体内的最外层,所以可以声明与函数的形式参数同名的变量,
19.                        同样会隐藏掉外层的变量n   */  
20.    } 
21.    ...  /* 另外一个 代码块 */  
22.    { 
23.        int i;  /* 代码块作用域,虽然上面的一个内层代码块中已经存在i,但是由于这两个代码块不存在嵌套关系,所以也不存在隐藏现象 */ 
24.    } 
25.} 
 
    注意事项:
 
         1.  注意函数原型中的参数是“函数原型作用域”,而函数定义中的参数是“代码块作用域”。例如上面代码中第一行的a,b和函数定义中的 n
 
         2.  由于函数定义中参数是“代码块作用域”,所以在函数体内的最外层的变量名称不能再为n,但是内层嵌套的代码块变量名称可以为n。虽然这条特性在某些较老版本的编译器中是可以的,但是在ANSI C中师不允许的。
 
         3.  变量的隐藏只是针对嵌套的作用域,对于不嵌套的作用域就没有这个说法。例如上面例子中的变量 f 是嵌套的,而 i 是不嵌套的,所以内层的 f 会隐藏掉外层的 f ,但是 i 不会相互隐藏。
 
2. 标识符的命名空间
 
        命名空间是为了解决 “在相同作用域内如何区分 相同的标识符”。
        说明:①只有在相同作用域的情况下才能使用到命名空间去区分标识符,在嵌套的作用域、不同的作用域区分标识符都用不到命名空间的概念。
                  ②在相同的作用域内,如果命名空间不同,标识符可以使用相同的名称。否则,即如果命名空间不同,编译器会报错,提示重复定义。
 
        按照C99(章节6.2.3),命名空间可以分为四种:
 
            2.1  所有的标签(label)都属于同一个命名空间。
                    说明:①在同一个函数内,你的标签不能相同。②在同一个函数内,标签可以和其他变量名称相同。因为它们所属的命名空间不同。
 
            2.2  struct、enum和union的名称,在C99中称之为tag,所有的tag属于同一个命名空间。
                   也就是说,如果你已经声明struct A { int a }; 就不能在声明 union A{ int a };
 
                   说明:之所以让所有的tag组成一个命名空间,由于Tag前面总是带struct,enum或union关键字,所以编译器可以将它们与其他的标识符区分开。
 
           2.3  struct和union的成员属于一个命名空间,而且是相互独立的。例如:如果你已经声明struct A { int a };
                  其成员的名称为a,你仍然可以声明 struct B{ int a };或者union B{ int a };
 
                  说明:之所以让struct和union的成员各自成为一个命名空间,是因为它们的成员访问时,需要通过 "."或"->"运算符,而不会单独使用,所以编译器可以将它们与其他的标识符区分开。由于枚举类型enum的成员可以单独使用,所以枚举类型的成员不在这一名称空间内。
 
           2.4  其他所有的标识符,属于同一个名称空间。包括变量名、函数名、函数参数,宏定义、typedef的类型名、enum的成员 等等。
                  注意:如果标识符出现重名的情况,宏定义覆盖所有其它标识符,这是因为它在预处理阶段而不是编译阶段处理。除了宏定义之外其它类别的标识符,处理规则是:内层作用域会隐藏掉外层作用域的标识符。
 
举例说明并分析:
 


view plaincopy to clipboardprint?
01.">#include <stdio.h>  
02.#include <stdlib.h> 
03. 
04.int main(){ 
05.    struct A{   /* “结构体的tag”和“结构体成员”不在同一个命名空间,所以名称可以相同 */  
06.        int A; 
07.    }; 
08.    union B{  /* 根据第二条,这个union的tag不能是A,但是根据第三条,其成员的名称可以与struct A的成员名称相同 */ 
09.        int A; 
10.    }; 
11.    struct A A; /* “结构体的tag”和“普通变量”不在同一个命名空间,所以名称可以相同 */  
12.    union B B;  /* 上面的“结构体变量”和 这行的“联合体变量”属于同一个命名空间,名称不能相同,即不能是 union B A */ 
13.    int my_label = 1; /* “普通变量”和“标签”不属于同一个命名空间,所以名称可以相同 */ 
14.    A.A = 1; 
15.    B.A = 20; 
16.    printf("B.A == %d  /n/n", B.A);      
17.     
18.  my_label:     /* 这里label 的名称与上面变量的名称 相同 */  
19.    printf("A.A == %d  /n", A.A); 
20.    A.A +=1; 
21.    if(A.A <= 5){ 
22.        goto my_label;     
23.    } 
24.     
25.    system("pause"); 
26.    return EXIT_SUCCESS; 
27.} 
 
运行结果为:
 


view plaincopy to clipboardprint?
01.B.A == 20 
02. 
03.A.A == 1 
04.A.A == 2 
05.A.A == 3 
06.A.A == 4 
07.A.A == 5 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/daheiantian/archive/2011/01/16/6219590.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值