常量
常量(Constant)是程序中最基本的元素,有字符(Character)常量、
整数(Integer)常量、浮点数(Floating Point)常量和枚举常量。
下面看一个例子:
printf("character: %c\n integer: %d\n floating point: %f\n", '}', 34, 3.14);
常量在程序加载内存的常量区,即.data段
字符常量要用单引号括起来,注意单引号只能括一个字符而不能像双引号那样括一串字符,
字符常量也可以是一个转义序列,
例如'\n',这时虽然单引号括了两个字符,但实际上只表示一个字符。
字符串字面值中使用转义序列有一点区别,如果在字符常量中要表示双
引号"和问号?,既可以使用转义序列\"和\?,也可以直接用字符"和?,
而要表示'和\则必须使用转义序列。[2]
计算机中整数和小数的内部表示方式不同,因而在 C 语言中是两种不同
的类型(Type),例如上例的 34 和 3.14,小数在计算机术语中称为浮
点数。这个语句的输出结果和 Hello world 不太一样,字符串
"character: %c\n integer: %d\n floating point: %f\n"并不是按原样打印输
出的,而是输出成这样:
character: }
integer: 34
floating point: 3.14
变量
变量(Variable)是编程语言最重要的概念之一,变量是计算机存储器
中的一块命名的空间,可以在里面存储一个值(Value),存储的值是可以随时变的,比如这次存个字符'a'下次存个字符'b',正因为变量的值
可以随时变所以才叫变量。
常量有不同的类型,因此变量也有不同的类型,变量的类型也决定了它
所占的存储空间的大小。例如以下四个语句定义了四个变量 fred、bob、
jimmy 和 tom,它们的类型分别是字符型、整型、浮点型:
char fred;
int bob;
float jimmy;
double tom;
声明和定义
C 语言中的声明(Declaration)有变量声明、函数声明和类型声明三种。
如果一个变量或函数的声明要求编译器为它分配存储空间,那么也可以
称为定义(Definition),因此定义是声明的一种。在接下来几章的示例
代码中变量声明都是要分配存储空间的,因而都是定义,在下一章我们
会看到函数的定义和声明也是这样区分的,分配存储空间的函数声明可
以称为函数定义。声明一个类型是不分配存储空间的。
“类型定义”和“类型声明”表示相同的含义。声明和语句类似,也是
以;号结尾的,但是在语法上声明和语句是有区别的,语句只能出现在{}
括号中,而声明既可以出现在{}中也可以出现在所有{}之外。
浮点型有三种,float 是单精度浮点型,double 是双精度浮点型,long
double 是精度更高的浮点型。给变量起名不能太随意,上面四个变量的名字就不够好,我们猜不出这些变量是用来存什么的。而像下面这样起
名就很好:
char firstletter;
char lastletter;
int hour, minute;
我们可以猜得到这些变量是用来存什么的,前两个变量的取值范围应该
是'A'~'Z'或'a'~'z',变量 hour 的取值范围应该是 0~23,变量 minute 的
取值范围应该是 0~59,所以应该给变量起有意义的名字。从这个例子
中我们也看到两个相同类型的变量(hour 和 minute)可以一起声明。
给变量起名有一定的限制,C 语言规定必须以字母或下划线_
(Underscore)开头,后面可以跟若干个字母、数字、下划线,但不能
有其它字符。例如这些是合法的变量名:Abc、__abc__、_123。但这
些是不合法的变量名:3abc、ab$。其实这个规则不仅适用于变量名,
也适用于所有可以由程序员起名的语法元素,例如以后要讲的函数名、
宏定义、结构体成员名等,在 C 语言中这些统称为标识符(Identifier)。
另外要注意,表示类型的 char、int、float、double 等虽然符合上述规
则,但也不能用作标识符。在 C 语言中有些单词有特殊意义,不允许用
作标识符,这些单词称为关键字(Keyword)或保留字(Reserved Word)。
还有一点要注意,一般来说应避免使用以下划线开头的标识符,以下划
线开头的标识符只要不和 C 语言关键字冲突的都是合法的,但是往往被
编译器用作一些功能扩展,C 标准库也定义了很多以下划线开头的标识符,所以除非你对编译器和 C 标准库特别清楚,一般应避免使用这种标
识符,以免造成命名冲突。
赋值
定义了变量之后,我们要把值存到它们所表示的存储空间里,可以用赋
值(Assignment)语句实现:
char firstletter;
int hour, minute;
firstletter = 'a';
/* give firstletter the value 'a' */
hour = 11;
/* assign the value 11 to hour */
minute = 59;
/* set minute to 59 */
注意变量一定要先声明后使用,编译器必须先看到变量声明,才知道
firstletter、hour 和 minute 是变量名,各自代表一块存储空间。另外,
变量声明中的类型表明这个变量代表多大的一块存储空间,这样编译器
才知道如何读写这块存储空间。还要注意,这里的等号不表示数学里的
相等关系,和 1+1=2 的等号是不同的,这里的等号表示赋值。在数学
上不会有 i=i+1 这种等式成立,而在 C 语言中表示把变量 i 的存储空间
中的值取出来,再加上 1,得到的结果再存回 i 的存储空间中。再比如,
在数学上 a=7 和 7=a 是一样的,而在 C 语言中后者是不合法的。总结
一下:定义一个变量,就是分配一块存储空间并给它命名;给一个变量
赋值,就是把一个值保存到这块存储空间中。变量的定义和赋值也可以
一步完成,这称为变量的初始化(Initialization),例如要达到上面代码
的效果也可以这样写:
char firstletter = 'a';int hour = 11, minute = 59;
在初始化语句中,等号右边的值叫做 Initializer,例如上面的'a'、11 和
59。注意,初始化是一种特殊的声明,而不是一种赋值语句。就目前来
看,先定义一个变量再给它赋值和定义这个变量的同时给它初始化所达
到的效果是一样的,C 语言的很多语法规则既适用于赋值也适用于初始
化,但在以后的学习中你也会了解到它们之间的不同,请在学习过程中
注意总结赋值和初始化的相同和不同之处。
例如:
int hour, minute;
hour = "Hello.";
minute = "59";
/* WRONG ! */
/* WRONG !! */
注意第 3 个语句,把"59"赋给 minute 看起来像是对的,但是类型不对,
字符串不能赋给整型变量。
既然可以为变量的存储空间赋值,就应该可以把值取出来用,现在我们
取出这些变量的值用 printf 打印:
printf("Current time is %d:%d", hour, minute);
变量名用在等号左边表示赋值,而用在 printf 中表示把它的存储空间中
的值取出来替换在那里。
表达式
表达式与语句的区别左结合与右结合
常量和变量都可以参与加减乘除运算,例如 1+1、hour-1、hour * 60 +
minute、minute/60 等。这里的+ - * /称为运算符(Operator),而参与
运算的常量和变量称为操作数(Operand),上面四个由运算符和操作
数所组成的算式称为表达式(Expression)。
和数学上规定的一样,hour * 60 + minute 这个表达式应该先算乘再算
加,也就是说运算符是有优先级(Precedence)的,*和/是同一优先级,
+和-是同一优先级,*和/的优先级高于+和-。对于同一优先级的运算从
左到右计算,如果不希望按默认的优先级计算则要加()括号
(Parenthesis)。例如(3+4)*5/6 应先算 3+4,再算*5,再算/6。
前面讲过打印语句和赋值语句,现在我们定义:在任意表达式后面加个;
号也是一种语句,称为表达式语句。例如:
hour * 60 + minute;
这是个合法的语句,但这个语句在程序中起不到任何作用,把 hour 的
值和 minute 的值取出来加乘,得到的计算结果却没有保存,白算了一
通。再比如:
int total_minute;
total_minute = hour * 60 + minute;
这个语句就很有意义,把计算结果保存在另一个变量 total_minute 里。
事实上等号也是一种运算符,称为赋值运算符,赋值语句就是一种表达式语句,等号的优先级比+和*都低,所以先算出等号右边的结果然后才
做赋值操作,整个表达式 total_minute = hour * 60 + minute 加个;号构
成一个语句。
任何表达式都有值和类型两个基本属性。hour * 60 + minute 的值是由
三个 int 型的操作数计算出来的,所以这个表达式的类型也是 int 型。同
理,表达式 total_minute = hour * 60 + minute 的类型也是 int,它的值
是多少呢?C 语言规定等号运算符的计算结果就是等号左边被赋予的
那个值,所以这个表达式的值和 hour * 60 + minute 的值相同,也和
total_minute 的值相同。
等号运算符还有一个和+ - * /不同的特性,如果一个表达式中出现多个
等号,不是从左到右计算而是从右到左计算,例如:
int total_minute, total;
total = total_minute = hour * 60 + minute;
计算顺序是先算 hour * 60 + minute 得到一个结果,然后算右边的等号,
就是把 hour * 60 + minute 的结果赋给变量 total_minute,这个结果同
时也是整个表达式 total_minute = hour * 60 + minute 的值,再算左边
的等号,即把这个值再赋给变量 total。同样优先级的运算符是从左到右
计算还是从右到左计算称为运算符的结合性(Associativity)。+ - * /
是左结合的,等号是右结合的。