auto、register、static、extern等存储类修饰符的区别

本文详细解析了C/C++中auto、register、static和extern等存储类修饰符的作用,包括标识符的链接特性、对象的生存周期以及如何正确使用这些修饰符。

auto、register、static、extern等存储类修饰符的区别 - [C/C++编程 ]

版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本声明 
http://kimva.blogbus.com/logs/19329180.html

一、标识符的链接(linkage)

(1)外部链接
表示在整个程序中(多个程序文件)是相同的函数或对象。常见的有,在函数体外声明的extern变量。
(2)内部链接
表示只在当前程序文件中是相同的函数或对象。其它程序文件不能对其进行访问。常见的有,在函数体外声明的static变量。
(3)无链接
一般声明在函数内部的auto、register变量、还有函数的参数,都是无链接。它的作用域是函数内部。

二、对象的生存周期(lifetime)
(1)静态生存周期
具有静态生存周期的所有对象,都是在程序开始执行之前就被事先创建和初始化。它们的寿命覆盖整个程序的执行过程。如在函数内定义了一个static变量,那第一次调用该函数后,该变量的值将会被保留,当第二次被调用时,该变量的值还是第一次调用结束时的值。
(2)自动生存周期
自动生存周期的对象的寿命由“对象定义所处在的大括号{}”决定。每次程序执行流进入一个语句块,此语句块自动生存周期的对象就会被创建一个新实例,同时被初始化。

三、存储类修饰符
(1)auto
auto修饰符只能用在函数内的对象声明。声明中有auto修饰符的对象具有自动生存周期。
在ANSI C中,函数内的对象声明在默认情况下有自动生存周期,所以在函数内声明时auto可省略。

(2)register
当声明对象有自动生存周期时,可以使用register修饰符。因此,register也只能用在函数内的声明中。
此关键字告诉编译器:此对象的存取应该尽量快,最好存储在CPU的寄存器中。然而,编译器不见得会这么做。
另外要注意的是,当一个对象声明为register,就不可使用地址运算符&了,因为它有可能被放到寄存器中。

(3)static
函数标识符如果被声明为static,就具有静态生命周期。
如果是定义在函数外,那么该对象具有内部链接,其它程序文件不能对其访问。
如果是定义在函数内,那么该对象具有无链接,函数外不能对其访问。
注意:static变量初始化时,只能用常量。

(4)extern
如果声明在函数外,那么该对象具有外部链接,能够在其它程序文件使用。但要注意它有可能会被函数内定义的重名的变量所隐藏起来。
如果声明在函数内,该对象具有何种链接取决于当前程序文件中定义在函数外的相同名字的对象。如果在函数外也定义了一下相同名字的static对象,则该函数内的对象具有无链接,否则具有外部链接。
extern的对象都具有静态生命周期。
使用extern时,注意不能重复定义,否则编译报错,如:
程序文件一:
extern int a = 10;  //编译警告,extern的变量最好不要初始化
程序文件二:
extern int a = 20;  //重复定义,应改为extern int a;
一般最好这样,如果需要初始化,可把extern修饰符去掉(但也不要重复定义),另外如果其它程序文件也需要用到该变量,可用extern来声明该变量。这样会比较清晰。

(5)缺 省修饰符
函数内,与auto相同;
函数外,与extern相同;

 



linkagelifetime
auto函数内no linkage自动
函数外语法错语法错
register函数内no linkage自动
函数外语法错语法错
缺省函数内no linkage自动
函数外external linkage静态
static函数内no linkage静态
函数外internal linkage静态
extern函数内与它在函数外所声明的一致静态
函数外external linkage静态

 

例子:
int func1(void);                        //func1具有外部链接;
int a = 10;                                //a具有外部链接,静态生存周期;
extern int b = 1;                      //b具有外部链接,静态生存周期。但编译会有警告extern变量不应初始化,同时也要注意是否会重复定义;
static int c;                              //c具有内部链接,静态生存周期;
static int e;                              //e具有内部链接,静态生存周期;
static void func2(int d){        //func2具有内部链接;参数d具有无链接,自动生存周期;
    extern int a;                        //a与上面的a一样(同一变量),具有外部链接,静态生存周期。注意这里的不会被默认初始为0,它只是个声明;
    int b = 2;                            //b具有无链接,自动生存同期。并且将上面声明的b隐藏起来;
    extern int c;                        //c与上面的c一样,维持内部链接,静态生存周期。注意这里的不会被默认初始为0,它只是个声明;
                                              //如果去掉了extern修饰符,就跟b类似了,无链接,自动生存周期,把上面声明的c隐藏起来;
    static int e;                        //e具有无链接,静态生存周期。并且将上面声明的e隐藏起来;初始化值为0;
    static int f;                        //f具有无链接,静态生存周期;
}

From : http://kimva.blogbus.com/logs/19329180.html


<think>好的,我现在要回答用户关于C语言存储类区别的问题。首先,我需要确认自己是否对C语言的存储类有足够的了解。存储类主要包括autoregisterstaticextern,可能还有typedef,不过typedef通常被归类为存储类说明符,但其实它主要用于定义类型别名,而不是直接管理存储。这点需要明确,避免混淆用户。 接下来,我应该逐个分析每个存储类的特性和区别。首先从auto开始,auto关键字用于局部变量,默认情况下函数内的变量都是auto存储类,所以通常不需要显式声明。它的生命周期局限于函数执行期间,离开作用域后自动释放。这部分需要解释清楚,但可能用户已经知道,所以可以简要说明。 然后是register,这个关键字建议编译器将变量存储在寄存器中,以提高访问速度。不过,现代编译器优化已经很智能,可能会忽略register的建议。另外,register变量不能取地址,因为寄存器没有内存地址。需要提到这些限制,并说明实际开发中已较少使用。 接下来是static,这个关键字有两个主要用途:在函数内部修饰局部变量,使其生命周期延长到整个程序运行期间,但作用域不变;在文件作用域修饰全局变量或函数,限制它们仅在当前文件内可见。这两点需要分开解释,并举例子说明。例如,函数内的static变量在多次调用中保持值不变,而static全局变量避免被其他文件引用。 extern关键字用于声明在其他文件中定义的全局变量或函数,帮助实现跨文件访问。需要解释extern的作用是扩展变量或函数的作用域,而不是定义新的实体。同时,说明在多个文件共享全局变量时如何使用extern,以及可能出现的重复定义问题。 此外,需要比较这些存储类的作用域、生命周期、存储位置等属性。例如,autoregister都是局部作用域,自动生命周期;static局部变量也是局部作用域但静态生命周期;static全局变量则是文件作用域,静态生命周期。extern变量具有外部链接,作用域可跨文件。 还需要考虑可能的误区,比如static在函数内外的不同作用,以及extern和全局变量的关系。同时,提醒用户typedef不是存储类,避免混淆。 最后,组织这些信息,确保结构清晰,分点说明每个存储类的特点,对比它们的区别,并给出使用场景的建议。可能需要使用例子来帮助理解,比如静态变量的计数器,extern在多文件项目中的应用等。检查是否存在错误,比如是否遗漏了某个存储类,或者是否有不准确的说法,比如register是否真的被弃用,或者编译器如何处理。确保信息准确,符合C标准,同时用中文简洁明了地表达出来。</think>C语言中存储类用于确定变量的作用域、生命周期和存储位置。主要存储类包括 **auto**、**register**、**static** 和 **extern**。以下是它们的区别和用途: --- ### 1. **auto** - **作用域**:局部于定义它的代码块(如函数内部)。 - **生命周期**:变量在进入代码块时创建,退出时销毁。 - **存储位置**:栈内存。 - **特点**: - 默认存储类(函数内未显式声明存储类的变量均为 `auto`)。 - 不可用于全局变量。 - 示例: ```c void func() { auto int x = 10; // 等价于 int x = 10; } ``` --- ### 2. **register** - **作用域**:局部于代码块。 - **生命周期**:同 `auto`,随代码块结束而释放。 - **存储位置**:建议编译器将变量存储在寄存器中(实际是否存储编译器决定)。 - **特点**: - 用于频繁访问的变量(如循环计数器),以提高速度。 - **不可取地址**(因为寄存器变量无内存地址)。 - 现代编译器通常自动优化,显式使用 `register` 已较少见。 - 示例: ```c void func() { register int i; for (i = 0; i < 1000; i++) { ... } } ``` --- ### 3. **static** - **两种使用场景**: #### (1) 局部静态变量(函数内部) - **作用域**:仍限于定义它的代码块。 - **生命周期**:程序运行期间持续存在(仅初始化一次)。 - **存储位置**:静态存储区。 - **特点**: - 变量值在多次函数调用间保持。 - 示例: ```c void counter() { static int count = 0; // 只初始化一次 count++; printf("%d", count); } // 首次调用输出1,第二次输出2,依此类推 ``` #### (2) 全局静态变量/函数(文件作用域) - **作用域**:仅当前文件可见(限制外部文件访问)。 - **生命周期**:程序运行期间。 - **特点**: - 避免命名冲突,实现封装。 - 示例: ```c static int hidden_var; // 仅本文件可用 static void hidden_func() { ... } // 仅本文件可调用 ``` --- ### 4. **extern** - **作用域**:跨文件可见。 - **生命周期**:程序运行期间。 - **存储位置**:静态存储区。 - **特点**: - 用于声明(非定义)其他文件中定义的全局变量或函数。 - 链接多个文件的全局变量。 - 示例: - **文件1.c**: ```c int global_var = 42; // 定义全局变量 ``` - **文件2.c**: ```c extern int global_var; // 声明外部变量 void func() { printf("%d", global_var); // 访问文件1的全局变量 } ``` --- ### 对比总结 | 存储类 | 作用域 | 生命周期 | 存储位置 | 主要用途 | |----------|----------------|----------------|------------|------------------------------| | `auto` | 代码块内 | 代码块执行期间 | 栈 | 默认局部变量 | | `register` | 代码块内 | 代码块执行期间 | 寄存器/栈 | 高频访问变量(已少用) | | `static` | 代码块/文件内 | 程序运行期间 | 静态存储区 | 保持值的局部变量、文件封装 | | `extern` | 跨文件 | 程序运行期间 | 静态存储区 | 跨文件共享全局变量或函数 | --- ### 使用建议 - **优先用 `auto`**:默认局部变量无需显式声明。 - **慎用 `static`**:避免滥用导致代码耦合。 - **多用 `extern` 管理全局变量**:明确跨文件依赖关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值