本节将介绍内存、变量和数据类型等基本概念,并通过一个实现温度转换的程序实例来介绍如何把数据存入内存、程序如何访问并处理这些数据。
计算机执行程序时,组成程序的指令和程序所操作的数据都必须存储在某个地方,这个地方就是机器的内存。
内存可以理解为一排井然有序的小开关,每个开关有两个状态:开表示为1,关表示为0。将每个开关表示为一个二进制数:0或1,称为一位(bit)。为管理方便,通常以8位为一组,称为一个字节(byte),是计算机数据存储空间的基本单位。而2 10 (1024)个字节称为1 KB,2 10 KB称为1 MB,2 10 MB称为1GB。
每台计算机都安装了一定数量的内存。计算机中的内存是逐字节排列的,每个字节的内存都对应唯一的地址,用于标识该字节,字节的地址最小值为0,最大值取决于系统的内存容量。一般使用者不必关心地址,地址是由C编译器自动处理的。程序2-1展示了一个通过变量来访问内存数据的例子。
/*程序2-1:实现将华氏温度转换为摄氏温度输出*/
程序运行时若输入:100
则输出如下:
fahr=100,celsius=37
celsius和fahr是变量名,各自对应着计算机内存中的一个存储单元(一个或多个字节)。程序通过变量名来读取某个内存单元中的数据或在其中存储一个新数据。
高级语言都能通过变量名来访问内存中的数据。用变量名来标识内存中的某个存储位置——由一个或多个连续的字节组成,在程序中使用变量名,实际上引用的是内存中对应的某个存储位置。
下面详细介绍程序2-1中每行代码的意义。
int celsius,fahr;
这条代码的作用是分配2个存储单元,名字分别为celsius和fahr。
scanf("%d",&fahr);
这条代码的作用是,读入一个十进制整数,存入名字为fahr的存储单元中;注意将一个值存入变量中是具有破坏性的,它将替换掉该内存位置中原来的数值。
celsius=5 * (fahr-32)/ 9;
读取fahr的值,进行算术运算,并将结果存入名字为celsius的内存单元中。
printf("fahr=%d,celsius=%d\n",fahr,celsius);
这条代码的作用是,用printf()函数输出内存变量fahr和celsius的值。
在C语言中,变量命名必须遵守以下规则:
·变量名可以由字母、数字和_(下划线)组合而成;
·变量名不能包含除_(下划线)以外的任何特殊字符,如:%、# 、逗号、空格等;
·变量名必须以字母或_(下划线)开头;
·变量名不能包含空白字符(换行符、空格和制表符称为空白字符);
·C语言中的某些词(例如int和float等)称为保留字,具有特殊意义,不能用作变量名;
·C语言区分大小写,因此变量a与变量A是两个不同的变量。
C89标准中规定变量名最多可以包含31个字符,给变量取名最好能做到“见名识义”。对于由多个单词组合而成的变量名,有多种风格,其中采用较多的一种是用下划线将单词连接,例如:student_name、student_score,如果不用下划线,那么应该将第一个单词后面的每个单词都用大写字母开头,如studentName、studentScore。编程者可以根据个人的喜好来选取命名风格,本书采用第二种方法。
(1)给变量起一个有含义的名字,可以使程序具有自文档性,增加程序的可读性;
(2)由多个单词组成的变量名能增加程序的可读性;
(3)给简单变量命名时,第一个字母一定要小写。
在C语言中引入不同数据类型的目的是便于在程序中按不同方式和要求处理数据。由于不同类型的数据在内存中占用不同大小的存储单元,可以参与不同的运算,即使同样是加法运算“+”,整数加法和浮点数加法在实现上也是完全不同的。
编写程序时,通常使用变量来存储数据,每个变量都具有三个属性,即变量名、数据类型和值。用变量来存储数据,系统就需要知道每个变量要存储什么类型的数据,然后为每个变量分配一块对应大小的内存空间,用来存储该类型的数据。
C语言的数据类型分为两个大类:第一类是基本数据类型,包括整型、浮点型和字符型。第二类是构造数据类型,包括数组、结构、联合和枚举。本节仅讨论整数及浮点型数据类型,其他数据类型将在后续章节陆续介绍。
C语言的整数类型有不同的取值范围,为了处理不同范围的整数,除了基本整型int以外,C语言提供了扩展的整数类型,它们的表示方法是在int之前加上限定词short、long或unsigned。
有符号整数如果是正数或零,那么最高位(符号位)为0;如果是负数,则符号位为1。因此,最大的16位整数的二进制表示形式是0111111111111111,对应的值是32 767(即2 15 -1)。而最大的32位整数是01111111111111111111111111111111,对应的值是2 147 483 647 (即2 31 -1)。
无符号整数表示正数或零,不带符号位,存储单元全部用来表示数值,最大16位无符号整数是65 535(即2 16 -1)。而最大的32位无符号整数是11111111111111111111111111111111,对应的值是4 294 967 295(即2 32 -1)。
默认情况下,C语言中的整型变量都是有符号的,若要告诉编译器变量为无符号类型,需要将其声明为unsigned类型。无符号整数类型主要应用于底层编程,在一般应用中,通常回避无符号整型。
为了选择正好满足需要的整数类型,可以指明变量是long类型还是short类型,是signed还是unsigned类型,也可以把说明符组合起来,如unsigned long int。C语言允许通过省略单词int来缩写整数类型的名字,如short int可以缩写为short,long int可以缩写为long。这种组合可以产生以下六种不同类型。C语言并未规定6种整数类型的长度,只要求short型不长于int型,int型不长于long型。表2-1列出了32位机中整型数据所占用的内存空间和所能表示的数的精度,这里int和long有着相同的取值范围。
表2-1 32位机的整数类型
在C语言中,使用变量之前必须先声明,变量的声明其实是将变量的名称和数据类型告诉编译器,可以在声明变量时为变量赋初值。如果使用一个未经声明的变量,编译器会生成一条错误消息。
变量声明的格式:
<变量类型> <变量名称>
变量的声明首先要指定变量的类型,然后说明变量的名称。例如:
int celsius;
这条代码说明变量celsius是一个int型变量,这意味着变量celsius可以存储一个整数。在C89的标准中,函数中变量的声明必须位于函数的左花括号之后,任何可执行语句之前。
在C语言中一行代码还可以同时声明多个变量。如程序2-1中第5行:
int celsius,fahr;
这条代码声明变量celsius和fahr都是int型变量。
在使用变量之前,一定要为其赋一个确定的值。没有默认值并且尚未在程序中被赋值的变量是未初始化的,如果试图访问未初始化的变量,可能会出现不可预知的结果。程序2-1中,用函数scanf()读入一个十进制整数存入fahr,就可以使用它进行计算然后为celsius赋值。也可以采用赋值语句为变量赋值,如
fahr=100;
就是为变量fahr赋值100。变量的值必须是在变量允许的数值范围之内。还可以通过计算为其赋值。例如:
celsius=5 * (fahr-32)/ 9;
(1)建议将变量声明部分与可执行语句部分,用一个空行分隔;
(2)不要使用未被初始化的变量,否则结果会不可控制。
程序2-1的第6行出现了数学中常见的一个符号“=”,在程序设计语言中一般称之为赋值运算符。
用“=”号将变量和一个表达式连接起来的式子,一般称为赋值表达式。
赋值运算符左边的变量代表一个内存地址值,通过这个内存地址,就可以对内存(变量)进行读写操作,C语言把这个左操作数称为“左值”,左值可以被赋值。
赋值运算符具有右结合性,正确理解赋值运算的最好方法是从右向左读。例如:
celsius=5 * (fahr-32)/ 9;从右向左读作“把表达式5 * (fahr-32)/ 9的值赋给变量celsius”。
赋值运算具有破坏性,通过赋值运算将一个新值存入到一个内存单元中,这个值将替换掉该存储单元里原来的旧值。
初学者常把赋值运算符与数学上的等号的意义混为一谈,在程序中常常造成错误。程序2-2的功能是求两个整数的和,程序中用到变量的赋值和引用,当赋值和引用顺序不同时,会产生不同的结果。
/*程序2-2:分析本程序为何不能得到正确结果*/
此程序运行的结果是屏幕上输出一个垃圾值,得不到正确结果,错误的原因是C语言的初学者,特别是首次接触计算机程序设计语言的初学者,很容易受代数知识的影响,误以为其中的sum=a+b是先行建立sum和a+b之间的等量关系,然后再将a和b的值代入等式就可以得到正确的结果。
实际上,在C语言中的“=”并不表示某种相等关系,而是表示赋值运算。上面程序中的语句
sum=a+b;
只是对sum进行了一次赋值,并没有使二者相等的意思。而且在给sum赋值时,a和b中存储的都是垃圾值,所以执行sum=a+b是将两个垃圾值相加赋给了sum,而后面的语句“a=3;”和“b=5;”对sum的值毫无影响。
所以赋值运算符“=”表示的是一个操作:先计算右边表达式的值,再存入作为“左值”的变量中,既不表示数学中的等量代换,也不表示相等关系。正确的写法应该是:
a=3;
b=5;
sum=a+b;
正常情况下,赋值运算符左右两端的值的数据类型相同,也就是将int型的值赋给int型变量,将float型的值赋给float型的变量。