c语言中定义变量的几个关键字

本文详细解析了C语言中的auto、static、register、extern、volatile和restrict等关键字的使用方法及特点,帮助读者理解不同存储类别的变量及其作用。

1.auto
(1)auto关键字在C语言中只有一个作用,那就是修饰局部变量。
(2)auto修饰局部变量,表示这个局部变量是自动局部变量,自动局部变量分配在栈上。(既然在栈上,说明它如果不初始化那么值就是随机的······)
(3)平时定义局部变量时就是定义的auto的,只是省略了auto关键字而已。可见,auto的局部变量其实就是默认定义的普通的局部变量。

2.static
(1)static关键字在C语言中有3种用法,而且这两种用法彼此没有任何关联、完全是独立的。其实当年本应该多发明一个关键字,但是C语言的作者觉得关键字太多不好,于是给static增加了一种用法,导致static一个关键字竟然有两种截然不同的含义。
(2)static的第一种用法是:用来修饰局部变量,形成静态局部变量。要搞清楚静态局部变量和非静态局部变量的区别。本质区别是存储类不同(存储类不同就衍生出很多不同):非静态局部变量分配在栈上,而静态局部变量分配在数据段/bss段上。
(3)static的第二种用法是:用来修饰全局变量,形成静态全局变量。要搞清楚静态全局变量和非静态全局变量的区别。区别是在链接属性上不同:静态全局变量的连接属性是文件内连接;非静态全局变量的连接属性是文件外连接。

(3)static的第二种用法是:用来修饰函数,将函数的作用域限制在本文件中,其他文件不可见。
分析:

   static的后两种作用本质是一样的,都是将全局变量或函数限制在本文件中。
    静态局部变量在存储类方面和全局变量一样,都是在数据段或bss段。
    静态局部变量在生命周期方面和全局变量一样,从程序开始到程序消亡。
    静态局部变量和全局变量的区别是:作用域、连接属性。静态局部变量作用域是代码块作用域(和普通局部变量是一样的)、      链接属性是无连接;全局变量作用域是文件作用域(和函数是一样的)、链接属性方面是外连接。

3.register
(1)register关键字不常用,也只有一个作用,那就是:register修饰的变量。编译器会尽量将它分配在寄存器中。(平时分配的一般的变量都是在内存中的)。分配在寄存器中一样的用,但是读写效率会高很多。所以register修饰的变量用在那种变量被反复高频率的使用,通过改善这个变量的访问效率可以极大的提升程序运行效率时。所以register是一种极致提升程序运行效率的手段。
(2)uboot中用到了一个register类型的变量,gd这个变量是用来存uboot的全局变量(gd就是global data)。因为这个全局变量在整个uboot中到处都被访问,所以定义成register的。
(3)平时写代码要被定义成register这种情况很少,一般慎用。
(4)register编译器只能承诺尽量将register修饰的变量放在寄存器中,但是不保证一定放在寄存器中。主要原因是因为寄存器数量有限,不一定有空用。

4.extern
(1)extern主要用来声明全局变量,声明的目的主要是在a.c中定义全局变量而在b.c中使用该变量。
(2)C语言中程序的编译时以单个.c源文件为单位的,因此编译a.c时只考虑a.c中的内容(不会考了b.c的内容),这就导致a.c中使用了b.c中定义的变量时在编译时报错。解决方案是声明
(3)应该在a.c中使用g_b之前先声明g_b,声明就是告诉a.c我在别的文件中定义了g_b,并且它的原型和声明的一样,将来在链接的时候链接器会在别的.o文件中找到这个同名变量。声明一个全局变量就要用到extern关键字

5.volatile
(1)volatile的字面意思:可变的、易变的。C语言中volatile用来修饰一个变量,表示这个变量可以被编译器之外的东西改变。编译器之内的意思是变量的值的改变是代码的作用,编译器之外的改变就是这个改变不是代码造成的,或者不是当前代码造成的,编译器在编译当前代码时无法预知。譬如在中断处理程序isr中更改了这个变量的值,譬如多线程中在别的线程更改了这个变量的值,譬如硬件自动更改了这个变量的值(一般这个变量是一个寄存器的值)
(2)以上说的三种情况(中断isr中引用的变量,多线程中共用的变量,硬件会更改的变量)都是编译器在编译时无法预知的更改,此时应用使用volatile告诉编译器这个变量属于这种(可变的、易变的)情况。编译器在遇到volatile修饰的变量时就不会对改变量的访问进行优化,就不会出现错误。
(3)编译器的优化在一般情况下非常好,可以帮助提升程序效率。但是在特殊情况(volatile)下,变量会被编译器想象之外的力量所改变,此时如果编译器没有意识到而去优化则就会造成优化错误,优化错误就会带来执行时错误。而且这种错误很难被发现。
(4)volatile是程序员意识到需要volatile然后在定义变量时加上volatile,如果你遇到了应该加volatile的情况而没有加程序可能会被错误的优化。如果在不应该加volatile而加了的情况程序不会出错只是会降低效率。所以我们对于volatile的态度应该是:正确区分,该加的时候加不该加的时候不加,如果不能确定该不该加为了保险起见就加上。

6.restrict
(1)c99中才支持的,所以很多延续c89的编译器是不支持restrict关键字,gcc支持的。
(2)restrict也是和编译器行为特征有关的。
(3)restrict只用来修饰指针,不能修饰普通变量。
(4)http://blog.chinaunix.net/uid-22197900-id-359209.html

 

 

<think>我们正在讨论C语言中的extern关键字。根据用户的问题,他/她想了解extern关键字的使用方法。结合提供的引用资料,我们可以总结出extern的基本概念、作用、用法以及一些注意事项。引用[1]介绍了extern的基本概念,并提到它用于跨文件共享数据和函数声明。引用[2]同样说明了extern用于声明在其他文件中定义变量或函数,并提到了基本概念。引用[3]详细解释了extern的作用:避免重复定义、实现跨文件访问、控制作用域和链接属性。同时给出了一个简单的示例:在file1.c中定义全局变量a,在file2.c中用extern声明后即可使用。引用[4]展示了一个头文件的写法,其中使用了extern "C"来避免C++编译时的名字修饰,确保C语言链接规范。引用[5]则通过一个例子说明,在另一个文件中使用函数声明时,即使不使用extern关键字(仅用函数原型)也可以,因为函数声明默认具有外部链接。但对于变量,则必须使用extern来声明。因此,我们可以将extern的用法归纳为以下几点:1.声明外部变量:在多个文件共享同一个全局变量时,在一个文件中定义变量(如inta;),在其他文件中使用extern inta;来声明,这样多个文件就可以共享同一个变量。2.声明外部函数:函数默认具有外部链接,所以在其他文件中使用函数时,只需要声明函数原型即可(也可以加上extern,但通常省略)。例如:externvoid func();或 voidfunc();3.在头文件中的使用:通常将全局变量和函数的声明放在头文件中,并在头文件中使用extern关键字(对于变量是必须的,对于函数可省略)。然后在需要使用的源文件中包含该头文件。4.在C++中与C混合编程时,使用extern"C"来确保C语言链接规范,防止C++编译器对函数名进行修饰。注意事项:-使用extern声明变量时不能初始化,否则就变成了定义(分配存储空间)。 -要避免重复定义,即全局变量只能在一个文件中定义(不使用extern),在其他文件中用extern声明。下面我们通过示例来具体说明。示例1:共享全局变量文件file1.c中定义全局变量:int global_var =10;//定义全局变量,分配存储空间文件file2.c中使用该全局变量:extern intglobal_var; //声明,告诉编译器这个变量在其他文件中定义voidprint_var() {printf("%d\n",global_var);}示例2:共享函数文件file1.c中定义函数:void func() {//函数体}文件file2.c中声明并调用://以下两种声明方式都可以extern voidfunc();//显式使用extern//或者voidfunc();//隐式外部链接voidcall_func() {func();}示例3:头文件中的extern在头文件global.h中:#ifndef GLOBAL_H#defineGLOBAL_Hexternint global_var;//声明全局变量voidfunc();//声明函数,extern可省略#endif然后在file1.c中定义全局变量和函数:#include"global.h"intglobal_var=10;//定义全局变量voidfunc(){ ...}在file2.c中:#include"global.h"voiduse_them() {printf("%d\n",global_var); //使用全局变量func();//调用函数}示例4:在C++中调用C函数(使用extern"C")假设我们有一个C语言编写的函数,在C++中需要调用,可以这样写头文件:#ifdef __cplusplusextern"C" {#endifvoidc_function();//以C语言方式链接#ifdef __cplusplus}#endif这样在C++编译时,函数名不会被修饰,可以正确链接到C编译的目标文件。总结:-extern关键字用于声明变量或函数是在其他文件中定义的。-对于变量,使用extern进行声明是必须的(除非该变量是静态全局变量)。-对于函数,函数声明默认带有extern属性,所以可以省略。-在头文件中使用extern声明全局变量,然后在源文件中定义,这样可以避免重复定义错误。-在混合编程时,使用extern "C"来确保C语言链接规范。相关问题:1. extern和static关键字在作用域上有何区别?2.使用extern声明变量时,如果两个文件都定义了同名变量会怎样?3.在头文件中定义全局变量(不使用extern)会导致什么问题?4.extern "C"的作用是什么?在什么情况下使用?</think>### C语言`extern`关键字详解 `extern`是C语言中的**存储类别修饰符**,用于声明在其他文件中定义变量或函数,实现跨文件共享数据。以下是具体用法和注意事项: --- #### 一、基本用法 1. **声明外部全局变量** 在`FileA.c`中定义变量,在`FileB.c`中用`extern`声明后即可使用: ```c // FileA.c int globalVar = 10; // 定义全局变量 // FileB.c extern int globalVar; // 声明外部变量 printf("%d", globalVar); // 输出: 10 ``` 2. **声明外部函数** 函数默认具有外部链接属性,`extern`可省略: ```c // FileA.c void func() { printf("Hello"); } // FileB.c extern void func(); // 等价于 void func(); func(); // 输出: Hello ``` --- #### 二、头文件中的规范用法 在头文件中用`extern`声明全局变量,在源文件中定义变量,避免重复定义: ```c // global.h #ifndef GLOBAL_H #define GLOBAL_H extern int sharedVar; // 声明(非定义) #endif // main.c #include "global.h" int sharedVar = 100; // 实际定义 // other.c #include "global.h" printf("%d", sharedVar); // 正确访问 ``` --- #### 三、混合编程(C++调用C代码) 使用`extern "C"`确保C++按C语言规范链接函数: ```c #ifdef __cplusplus extern "C" { // 告诉C++编译器:内部是C函数 #endif void c_func(); #ifdef __cplusplus } #endif ``` --- #### 四、注意事项 1. **禁止初始化** `extern`声明时不能初始化变量,否则变为定义: ```c extern int errVar = 5; // 错误!导致重复定义 ``` 2. **作用域限制** `extern`声明的变量必须是全局作用域: ```c void foo() { extern int localVar; // 错误!不能在局部作用域声明 } ``` 3. **与`static`对比** | 关键字 | 作用域 | 链接属性 | |--------|--------------|--------------| | `extern` | 跨文件 | 外部链接 | | `static` | 仅当前文件 | 内部链接 | --- #### 五、典型错误示例 **错误场景**:头文件中直接定义变量 ```c // error.h int var = 10; // 包含该头文件的多个.c文件会重复定义 ``` **解决方案**:头文件改用`extern`声明,源文件定义。 --- ### 相关问题 1. `extern`声明变量时,为什么不能初始化? 2. 如何在多个文件中安全地共享同一个全局变量? 3. `extern "C"`在C++中起什么作用?与普通`extern`有何区别? 4. 使用`extern`时如何避免链接错误? > 引用来源: > [^1]: C语言extern关键字技术详解 > [^2]: 【C语言】extern 关键字详解 > [^3]: C语言extern关键字详解 > [^4]: C语言extern关键字用法和理解 > [^5]: c语言extern关键字的理解和使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值