购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.10 变量

前面已经多次使用过变量了,本节将详细地介绍一下变量。变量是相对常量而言的,常量是指在程序运行过程中固定不变的量,而变量顾名思义就是在程序运行过程中可变的量。前面说过,变量就是一个“容器”,将数据保存到计算机中就是通过变量保存的。那么这个容器的本质是什么呢?它是在计算机中的什么地方呢?它是如何工作的呢?为什么需要变量呢?在回答这些问题之前首先要弄清楚变量是如何定义的。

5.10.1 如何定义变量

定义变量的格式非常简单,如下所示:


   数据类型
   变量名;
  

首先要强调的一点是:最后的分号千万不要丢了。变量的定义是一个语句,我们说过,语句都是以分号结尾的。

“数据类型”表示想要存储什么类型的数据就定义什么类型的变量。想要存储整数就定义成int型;想要存储小数就定义成float型或double型;想要存储字符就定义成char型……

“变量名”就是你想给这个变量起个什么名字,通常都是用字母。比如:


int i;

就表示定义了一个整型变量i。变量名的命名规则稍后会详细地讲。那么变量定义好之后怎么将数放到这个变量里面呢?首先要理解的是,将一个数字放到一个变量中,这个动作叫“赋值”。“赋”即“给予”的意思,所以“给变量赋值”意思就是将一个值传给一个变量。那怎么赋值呢?是通过赋值运算符“=”,赋值的格式是:


   变量名 = 要赋的值;
  

它的意思是将“=”右边的数字赋给左边的变量。比如:


i = 3;

这就表示将3赋给了变量i,此时i就等于3了。

这里需要注意的是,这里的“=”跟数学中的“等于号”是不一样的。在刚开始学习C语言的时候,在这一点上大家很难从数学的思维中转变过来。在C语言中“=”表示赋值,即将右边的值赋给左边的变量,而不是左边的变量等于右边的值。

那C语言中有没有表示相等的运算符呢?这就是双等号“==”。这个运算符就与数学中的“等于”是同一个意思了。

以上就是变量的定义和赋值,它们是分成两步写的。也可以将它们合成一步,而且事实上,在实际编程中用得最多的也是合二为一的写法。那么怎么合在一起呢?很简单,形式如下:


   数据类型
   变量名 = 要赋的值;
  

比如:


int i = 3;

就表示定义了一个变量i,并把3赋给这个变量。它与


int i;
i =3;

是等价的。

在定义变量时也可以一次性定义多个变量,比如:


int i, j;

这就表示定义了变量i和j。这里需要强调的是,当同时定义多个变量时,变量之间是用逗号隔开的,千万别写成分号。这是很多新手最容易犯的错误,即将逗号和分号记混了。

同样也可以在定义多个变量的同时给它们赋值:


int i = 3, j = 4;

中间还是用逗号隔开,最后别忘记输入分号。

最后需要注意的是,变量只能在程序的开头定义,或者说变量定义的前面不能有其他非声明或非定义的语句。比如在C语言中像下面这种写法就是错误的:


# include <stdio.h>
int main(void)
{
    int i;
    i = 3;
    int j = 4;  //这句是错误的,因为在它前面有一个给变量i赋值的非定义语句
    return 0;
}

编译的时候会提示这样的错误:“missing';'before'type'”。但是这种写法在.cpp文件中是允许的,.cpp文件中可以在程序的任意位置定义变量。其实这与语言本身并没有什么关系,并不是说C语言中变量就不能定义在程序中间。这实际上是与标准有关系的,有些编译器使用的是C89标准,C89标准要求所有声明(比如函数声明、变量定义)必须要写在程序、函数或复合语句的开头,而C99的新特性允许语句和声明按任意顺序排列,只要遵循“先声明后使用”的原则就行。

因为VC++6.0遵循的是C89标准,或者说它不能完全支持C99标准,所以它不允许将变量定义在程序中间;而Linux中gcc编译器遵循的是C99标准,所以同样是.c文件,同样是编写C语言,它就可以将变量定义在程序的任意位置,只要是在使用位置之前即可。其实这样是比较好的,变量需要的时候才定义,缩短了它的生命周期,节省了内存。因为只要定义变量,系统就会为它分配内存空间,所以如果在前面定义了但是到最后才使用它,那么在使用之前它一直占着内存不放,有点浪费。

但是尽管如此,大家在编程的时候还是尽量按照C89的标准编写,即使在完美支持C99标准的编译器中也最好不要将声明写在程序的中间。因为C89历时较长,现在它仍然是主流,很多编译器仍然只支持C89标准或不能很好地支持C99标准。所以按C89标准编写可移植性更强。

上面提到“复合语句”,在复合语句中也可以定义变量。所谓语句就是以分号结尾的,而复合语句就是多个语句组合在一起的语句。在C语言中,用大括号“{}”括起来的多个语句称为复合语句。复合语句在流程控制中用得特别多,比如后面将要学习的if、for、while一般都是跟复合语句的。此时在这些复合语句中也可以定义变量,但是也只能定义在它的开头。主函数main下面也是用大括号括起来的,所以主函数main下的函数体本质上也是一个复合语句,是一个大的复合语句。所以它们还是有共性的,也就是说,在C语言中,只要是用大括号“{}”括起来的,那么在它的开头都可以定义变量。而且这些在大括号中定义的变量都称为局部变量。此外,在大括号外面也可以定义变量,叫全局变量。这个我们就先不讲了,到后面再讲。

5.10.2 变量的本质

那么到底什么是变量?看看下面这个程序:


# include <stdio.h>
int main(void)
{
    int i;
    i = 3;
    printf("i = %d\n", i);
    return 0;
}

在上面这个程序中,首先定义了一个变量i,然后把3放到i里,接着输出i。关键看前两句,第三句printf的用法非常复杂,功能非常强大。但是因为其用法太灵活了,所以很容易出错。在后面会专门用一章的内容详细地介绍printf,这里先不讲。

“int i;”表示定义了一个变量i。那么程序在运行时定义的变量i存储在什么地方呢?存储在内存还是硬盘上?答案是存储在内存。当单击编译、链接、执行后弹出一个黑色窗口,关闭那个黑色窗口后,就意味着这个程序运行了一遍,而且这一遍已经终止了。此时存储3的内存空间就没有了,要是有的话就麻烦了!如果任何软件运行一遍后为它分配的空间仍保留着,那么计算机再运行几次就不行了。程序运行完之后,为这个程序所分配的所有内存空间通通都会被释放掉,以便被下一个程序使用。

所谓“释放”并不是指清空该内存空间,而是指将该内存空间标记为“可用”状态,使得系统在分配内存的时候可以将它重新分配给其他变量使用。如果没有释放的话系统在分配内存的时候是不会使用该内存空间的,因为它已经被别的变量使用了,是不可用的状态。“释放”这个词后面会经常说到,大家要注意一下。

下面看看这两条语句是怎么运行的。首先看程序是如何运行“int i;”的:当VC++运行第一条语句的时候,它会请求操作系统在内存中寻找一个空闲的存储单元,然后把它当作变量i来使用。也就是说这个存储单元的地址和i产生了一种关联。即变量i现在就是这个存储单元,这个存储单元现在就是变量i。

然后“i=3;”的结果是把3存放到变量i所关联的那个存储单元中。

前两条语句就是这么运行的。以后只要使用i,操作系统就会自动找到那个与它关联的存储单元。

5.10.3 为什么要使用变量

为什么要使用变量?如果不使用变量的话,那就意味着我们要自己编程寻找内存里面的那个存储单元。而内存里面有很多存储单元,每个存储单元都有一个编号。如果须记住这些编号的话就太麻烦了。假如要定义100个变量,那就要记住100个编号,这样编程是不可能实现的。而利用变量就可以很方便地解决这个问题!当定义了一个变量之后,此时变量对应的是哪个存储单元,那个存储单元的编号是什么,通通都不用我们考虑,系统内部会自动产生一一对应的关系。这样就方便了对数据的存储和对内存单元的操作。

5.10.4 变量的命名规则

在定义变量时,变量名可以是字母、数字和下划线的组合。但是也不是随便的组合,要注意以下几个命名规则:

1)变量名的开头必须是字母或下划线,不能是数字。实际编程中最常用的是以字母开头,而以下划线开头的变量名是系统专用的。随便打开一个头文件 就会看到,它里面所有的变量名、宏名、函数名全是以下划线开始的。所以为了避免与系统定义的名字产生冲突,在编程的时候,除非要求这么定义,否则永远都不要使用下划线作为一个变量名的开头。

2)禁止取单个字母作为变量名(如a、b、c、d、i、j、k、m、n……)。在前面的讲解中之所以用单个字母,只是为了方便讲解,以免喧宾夺主。刚开始学的时候也可以用,但是不要永远就只会用单个字母。

那为什么不要定义成单个字母呢?原因很简单——没有含义!你定义变量a,其他人怎么知道这个a是什么意思?就算有注释,但难免会忘记。

所以在定义变量的时候,变量名最好要有明确的含义,这样可以提高代码的可读性。不管是自己写程序,还是别人读你的程序,都会很舒服。在实际编程中经常用英文单词或英文单词的缩写来作为变量名。而且缩写也有一定要规则,通常较短的单词可通过去掉“元音”形成缩写,比如表示“个数”的count可以缩写成cnt;较长的单词可取单词中的几个字母形成缩写;对于一些单词还有大家公认的缩写,比如:

temp可缩写为tmp;//temp是“临时”的意思

flag可缩写为flg;//flag是“标志位”的意思

statistic可缩写为stat;//statistic是“统计”的意思

increment可缩写为inc;

message可缩写为msg;

如果变量名由多个单词组成而且不用缩写,那么每个单词的首字母全部都要用大写,必要时可用下划线分隔。

3)但并不是说就绝对不可以用单个字母,有些变量它本身就是没有含义,我们也不能硬生生给它赋予含义,这时候就可以用单个字母。比如后面学习循环语句的时候,里面有一个循环变量,它就是定义成i、j、k,这已经是公认的了。但大多数情况下,在编程的时候变量都是有含义的,这时候就应该用英文单词或英文单词的缩写作为它的名字。

4)变量名中的字母是区分大小写的。比如a和A是不同的变量名;num和Num是不同的变量名。

5)变量名绝对不可以是前面提到的32个关键字,这一点一定要记住!

6)变量名中不能有空格。这个可以这样理解:因为我们说过,变量名是字母、数字、下划线的组合,没有空格这一项。

5.10.5 为什么必须要初始化变量

这个问题很重要!实际上前面讲那么多,其中有一个很重要的目的就是为了讲这个知识点。所谓初始化就是“第一次赋值”的意思。我们先来看一个程序:


# include <stdio.h>
int main(void)
{
    int i;
    printf("i = %d\n", i);
    return 0;
}

这里i没有被赋值,只是定义了一个存储空间。这时输出这个空间的内容,你觉得输出的值会是多少?是一个任意值吗?可以通过编译、链接、执行看一下,如图5-3所示。

图5-3 显示一个警告

这时单击图5-3中圆圈标出的向上的箭头,就可以看到警告的原因,如图5-4所示。

图5-4 显示警告的原因

然后对着警告的那一行双击,在程序中就会标出是哪个地方发出警告:


# include <stdio.h>
int main(void)
{
    int i;
    printf("i = %d\n", i);
    return 0;
}

或者直接按F4键就可以直接在程序中标出警告的地方。如果是error报错的话,同样可以这么操作。

但需要注意的是:首先,图5-4中警告的原因只是软件自动检测出来的,不一定是真正警告的原因;其次,标示出来的警告位置也不一定是真正警告的位置。因为软件不可能百分之百的智能化,它只能推测。所以如果在标示出的位置找不到错误,那就往上几行或下几行看看。

现在来看一下它警告的内容是什么:


   warning C4700: local variable 'i' used without having been initialized
  

warning是警告;C4700表示的是一个地址;local是“本地的”,就是“局部的”意思。我们看到警告的原因是,“局部变量i使用了,但是没有给它初始化”,也就是说未初始化就使用它了。我们先不管警告,直接链接、执行,看结果是什么:


/*VC++ 6.0中的输出结果是:
--------------------------------------
i = -858993460
--------------------------------------
*/

如果i的值是任意值的话,那么再单击一次“执行”显示的应该是另一个值。但事实上不管重新执行多少次,还是这个值,这个值是不变的。下面就来讨论一下原因。

前面讲过,“int i;”运行的结果是将变量i和内存中的某一个单元产生关联。然后变量i就代表那个内存单元了,要想操作该内存单元只需要通过变量i就可以了。在上面程序中没有给这个内存单元赋值就把它给输出了,所以问题就出在这里。你只定义了变量i,但是没有给它赋值并把它输出了。所以关键就是看看i里面放的到底是什么。

首先,内存条是一个硬件设备,硬件设备里面要么是0要么是1。计算机肯定得通电,没有电就运行不起来,有电之后它就会通过各种二极管、三极管之类的元器件产生高低电平。如果电压在某一个范围之上,就认为是1,在某一个范围之下,就认为是0。所以1就表示高电平,0就表示低电平,即电压的高或低产生1或0。这时候只有两种状态,要么是1,要么是0。所以说i里面存放的就是由0和1组成的代码,不可能里面什么都没有!因为计算机要工作肯定得有电,有电就不可能是空的,有电就会转化成1或0这样的高低电平。所以硬件上要么是1要么是0,不可能既不是1也不是0,什么都没有!计算机硬件没有那种状态,这也是为什么计算机只能识别二进制的原因。所以变量i所关联的那内存单元里面肯定是由0和1组成的代码。

这里i单元虽然没有给它赋值,但这个空间在之前可能被其他软件使用过。比如放电影的时候将电影中的某一部分数据放到了这个单元中。电影看完之后一关闭,这个单元空间就释放了,即操作系统就会回收该内存空间,以便分配给其他软件使用。但需要注意的是,操作系统虽然回收了该内存空间,但它并不清空该内存空间中遗留下来的数据,这句话很重要。但这样的话就会出现一个问题:以前的软件如果使用过i这个空间的话,那么那些垃圾数据还在里面保留着,如果没有初始化,那么它里面就应该是一个随机的垃圾值。但是当我们多次执行的时候发现它不是一个随机的值,而是一个不变的值。这是为什么呢?这个值叫“填充数字”或叫“填充字”。

微软公司在设计VC++这个软件的时候,在它内部添加了一些特殊的处理。即如果发现一个变量里面存放的是一个垃圾值,就认为没有给它赋初值,那么系统就会自动将一个很小的,如–858993460这个填充数字给放进去。所以我们看到的结果都是一个不变的值。

那么微软公司为什么要这么设计?首先放入填充数字一定有好处!由于放入填充数字肯定会导致程序运行速度变慢,因为内部要进行处理。所以如果没有好处的话微软公司肯定不会这样设计!大家想想,如果之前使用i这个空间后遗留下来的数字是5,那么输出5后,你可能以为i里面存放的是一个有效的数据。但如果是一个很小的数字,这个数字基本上是很少有人使用的,那么你一看就知道可能是没有初始化。所以这个数字就是提醒你变量没有初始化。

综上所述,任何一个变量在使用之前必须要先对它进行初始化。不初始化的话使用它就没有实际意义了。在实际编程中,习惯上在定义变量的时候就对它进行初始化,这是一个很好的编程习惯。但是按照C89标准,变量的定义只能在程序或复合语句的开头。这样就会有一个问题:“很多在开头定义但到后面才会用到的变量,在定义的时候如何对它们进行初始化呢?”。此时习惯上将它们初始化为0,然后等后面真正用到它们的时候再重新赋值。当然,定义时不初始化也可以,等到后面知道该给它赋什么值的时候再给它赋值也行。但还是建议大家在定义时将它们初始化为0,养成这个习惯。

5.10.6 小结

变量的本质就是内存中的一段存储空间。变量非常的重要,它是学习C语言的基础。不仅是C语言,它也是学习任何一门语言的基础。因为不管什么语言都要存储数据,要存储数据就必须要使用变量。对于变量我们要掌握以下几点:

1)掌握定义变量的两种形式。如果同时定义多个变量,变量之间要用逗号隔开。变量的定义是一条语句,最后的分号千万不要忘记。

2)养成定义变量时就对它进行初始化的习惯。理解为什么未初始化的变量里面放的是–858993460。

3)初始化时要分清赋值运算符“=”和数学中等号的区别。

4)要理解变量的本质,知道变量和内存单元之间的关系,以及内存单元是如何通过变量存储数据的,从而理解为什么需要变量。

5)掌握变量的命名规则。变量名主要由字母、数字、下划线组合而成。不能用数字开头,最好不要用下划线开头。在没有特殊要求的前提下,命名变量时都以字母开头。要学会使用英文单词或英文单词的缩写给变量命名。

6)要学会在用VC++编译时如何查找错误。按F4键即可在程序中大概地标记出警告或错误的位置。

对于变量先理解到这里就可以了。在后面还会继续讲什么是变量、什么是内存的问题。 GM9YZCDLDc8Izzemwx78WGgCGphGZhxrXJchMTSuixneHVMfQrkm/a+lG1jv64lb

点击中间区域
呼出菜单
上一章
目录
下一章
×