1. 概述
C语言中,变量是个很重要的概念,前面的章节里很多地方都用到了变量,可见变量无处不在。变量是存储数据的一块可操作的内存区域。我们通过变量名就可以引用这块内存区域,可以获取存储于里面的数据(值),且在运行期间值是可以更改的。简言之,变量就是为了存储数据。
2. 变量名
C语言是强类型编译语言,对变量名有严格的规范要求:
- 变量名只能由大小写字母、数字和下划线“_”组成。
- 变量名区分大小写;
- 不能以数字开头;
- 长度不能超过63个字符;
- 不能使用关键字和保留字;
- 注意变量名的书写风格规范。这个没有硬性要求。
下面是一些变量声明的示例:
//有效变量名
short A;
short a;//和A是不同的变量
int a1;
long _b66;
bool is_true;
//无效变量名
int 6a;
int #123;
int a-b;
int int;
3. 变量类型
声明与定义变量必须指定数据类型,否则会报错;每个变量都有特定的数据类型,类型规定了变量存储的占用空间大小和数据类别。关于数据类型,可参考前面的文章:C语言教程(三)数据类型
下面是数据类型使用的例子:
int a;//声明为int类型的变量
short int b;//声明为short int类型的变量
float f;//声明为float类型的变量
4. 变量声明与定义
4.1 变量的声明
声明的作用就是把变量的信息告诉编译器,编译器会为该变量分配内存空间,使用变量说明符extern的情况下,则不会为其分配空间,因为变量是在别的文件中定义过的。
int a;//声明变量a,告诉编译器声明为int类型的变量,编译器开辟2个或4个字节的空间
long l;//声明变量l,告诉编译器声明为long int类型的变量,编译器开辟4个字节的空间
double d;//声明变量d,告诉编译器声明为double类型的变量,编译器开辟8个字节的空间
如果有数据类型相同的变量,则可以一起声明:
int a,b,c;//同时声明a,b,c三个变量,且都是int类型
int a;//等同于上面的int a,b,c;
int b;
int c;
4.2 变量的定义(初始化)
C语言中,对于全局变量和静态变量,变量声明后编译器会给变量初始化为一个默认值:
- 整型变量:默认值初始化为0;
- 浮点变量:默认初始化为0.0;
- 字符类型:默认初始化为’\0’;
- 指针类型:默认初始化为NULL;
- 其他高级类型:根据类型成员默认初始化。
局部变量不会初始化为默认值,初始值是未定义的,所以我们要显示地指定一个初始值:
变量的定义就是声明后使用赋值运算符“=”给变量显示地赋初始值,变量的值应和类型一致:
int a;//声明变量a
a = 5;//初始化为5
变量的声明与赋值也可以写在一行:
int a = 5;//等同于下面的赋值
int a;
a = 5;
和变量声明一样,对多个相同类型变量写在同一行赋值:
int a,b;
a = b = 5;
int a = 3,b = 5;//在一行赋值
5. 变量的分类(作用域)
在C语言中,根据变量定义的位置不同,可分为全局变量、局部变量。而变量的作用域就是变量的有效范围,它表示了变量的可见范围和声明周期。
5.1 全局变量
全局变量是指在函数外部定义的变量,存储在静态存储区,从定义的位置开始到整个程序结束,都是有效的,而且可以被其他任何函数访问,常用于共享数据,下面是一个全局变量的示例:
#include <stdio.h>
int a = 5;//定义全局变量
int main(void){
printf("%d\n",a);//在main函数中访问
}
5.2 局部变量
局部变量通常定义在函数体内或者代码块中,一般在函数内部、代码块中或者for循环块作用域中使用,其他函数无法访问,所以局部变量的作用域就是当前的函数或者代码块,随着函数的运行时期而存在。
int main(void){
int a = 1;
if(a == 1){//if代码块
int b = 2;
}
printf("%d\n",a);//输出1
printf("%d\n",b);报错,找不到变量b
}
代码块还可以嵌套,就是代码块包含代码块,这就形成了多层作用域,内层的代码块可以使用外层代码块定义的变量,而外层代码块则不能使用内层代码块的变量,而且还要遵循就近原则,即如果变量名重复,则内层代码块的变量会覆盖外层代码块的变量。
int a = 1;
if(a == 1){
int b = 2;
printf("%d\n",c);//报错
if(b == 2){
int b = 3;
int c = 4;
printf("%d\n",a);//输出1
printf("%d\n",b);//b重复变量名,读取当前代码块中的b,输出3
}
}
C语言中,静态变量分为局部静态变量和全局静态变量,都存储在全局静态存储区,也是编译时就分配空间。静态变量的生命周期也与整个程序的运行周期相同。C语言提供了static关键字来定义静态变量。
全局静态变量,作用域仅限于当前的源文件内部,在其他源文件中是访问不到的
6. 变量说明符
变量说明符就是在变量类型前面添加特定的一个说明符,作为变量的附加信息,可以改变变量的行为和方式。
6.1 static说明符
C语言中规定,使用static关键字可以声明静态变量,静态变量又分为全局静态变量和局部静态变量,都存放于全局静态存储区,在编译时就会为变量分配内存空间,生命周期同整个程序的执行而存在。
- 全局静态变量,作用域仅限于当前的源文件内部,只能在当前文件使用,在其他源文件中是访问不到的;如果没有显式初始化,编译器则会提供默认值。
- 局部静态变量,作用域在函数内部定义,函数首次被调用时进行初始化,并在函数调用期间保留变量值,函数调用结束后不会被销毁,下次调用时可以继续使用,编译器也会提供默认值。
static int a = 1;//声明全局静态变量
int main(void){
static int b = 2;//静态局部变量
}
6.2 const说明符
const说明符用于修饰某个变量为(伪)常量,它是只读的,不能被修改。如果更改变量值的话,编译器会报错。
const float f = 3.14f;//只读
f = 3;//报错
const修饰数组时,数组成员的值不能修改,数组的内容在后面章节会讲到
const int arr[] = {1, 2, 3};
arr[0] = 1; // 报错
const修饰指针变量时,有两种表示形式,如果const说明符在指针操作符*前面,表示指针指向的值不可以修改,称为常量指针;如果const说明符在指针操作符*
后面,指针本身就是个常量,表示指针本身的值(包含的地址)不可以修改,而不是指针指向的那个值,也称为指针常量。
//const在*前面,常量指针
int const * a;//*a不能更改
const int * a;//和上面的写法一样
//const在*后面,指针常量
int* const a;//a不能更改
6.3 extern说明符
extern说明符用于变量共享,给变量加上extern说明符,就是通知编译器在其他文件中寻找该变量或者函数的定义,可以在多个文件中共享。在使用了extern说明符后,编译器不会再为该变量分配内存了。
带extern说明符的变量声明是无效的,这是为了防止对同一个变量进行初始化,而且同时初始化也会失效,其实extern也相当于引入的意思。
extern int a;//带有extern说明符的变量,在其他文件中已经定义,不必分配内存
extern也同样适用于函数,在一个文件中使用另一个文件中定义的函数:
extern void func();//在其他文件中定义
6.4 register说明符
register修饰的变量是寄存器变量,就是告诉编译器把该变量存放于寄存器中,但是这不是强制性的,编译器可以忽略这个register说明符,而且它的作用域只能在代码块中,因为变量要存放于寄存器中,因此也不会有内存地址。如果将数组设置为register类型,则整个数组及其成员都不能获取地址。
register int a;//这是一个寄存器变量
register int a[] = {1,2,3,4,5};//这是一个寄存器数组
int *p = &a;//没有内存地址,获取失败,编译器报错
int p =a;//报错,没有地址
7. 小结
- 变量是为了更方便地存储数据;
- C语言规定对变量名有命名规范,书写风格也尽量遵守;
- 声明变量必须指定数据类型,否则会报错;
- 变量分为全局变量和局部变量,全局变量默认值会为0,而局部变量则是未定义;
- 常用的变量说明符有static、const、extern、register,此外还有auto、volatile、restrict等,很少用到。