Appearance
第二部分:数据类型与表达式
一、数据类型
1.常量
程序运行中值不能被改变的量称为常量。数值常量就是数学中的常数。
1.1.整型常量
整数即为整型常量。
1.2.实型常量
实数即为实型常量。常量有两种表示形式:
第一种是十进制小数形式,由数字和小数点组成,如:123.456
,0.345
等。第二种是指数形式,如 12.34e3
表示 12.34 x 103
。由于计算机输入时无法表示上下角标,所以规定字母 e
或 E
代表以 10
为底的指数。需要注意的是,e
或 E
之前必须有数字,e
后面必须为整数。
1.3.字符常量
a.普通字符
用单撇号括起来的单个的一个字符称为普通字符常量,如 'a'
,'Z'
。但 'ab'
,'1a2'
不是字符常量。字符常量在计算机中存储时,不是以字符形式存储,而是以 ASCII 代码形式存储。例如 a
的代码是 97
。ASCII 字符对照表见附录。
b.转义字符
除了正常的字符之外,C 语言中还有一种特殊形式的字符常量,称为转义字符。转义字符是为了表示一些无法直接用字符表达的内容而存在的。例如在输出函数中,'\n'
代表一个换行符。常用的转义字符如下表:
转义字符 | 字符值 | 输出结果 |
---|---|---|
' | 一个单撇号(' ) | 输出具有此八进制码的字符 |
' | 一个双撇号(" ) | 输出此字符 |
? | 一个问号(? ) | 输出此字符 |
\ | 一个反斜线(\ ) | 输出此字符 |
\a | 警告(alert) | 声音或视觉信号 |
\b | 退格(backspace) | 后退一个字符 |
\f | 换页(form deed) | 将当前位置移至下一页开头 |
\n | 换行 | 将当前位置移至下一页开头 |
\r | 回车(carriage return) | 将当前位置移至本行的开头 |
\t | 水平制表符 | 将当前位置移至下一个 tab |
\v | 垂直制表符 | 将当前位置移至下一个垂直制表对齐点 |
表:常用的转义字符
c.字符串常量
如 "boy"
,"123"
等用双引号括在一起的多个字符称为字符串。
d.符号常量与预定义指令
用 #define
指令,在程序开始的时候讲程序中的一个符号替换成一个常量。例如:
c
#define PI 3.14
使用预定义指令的好处在于,能在程序某个变量需要修改的时候做到改动一处就能修改全局。这个时候有一个需要思考的问题,例如有两行代码:
c
#define PI 3.14
int PI = 3.14;
当我们想要修改 PI
的值时,同样都可以做到“一改全改”,那么这两种方法有什么区别呢?区别是这样的,#define
预定义指令相当于我们直接在代码上进行“查找与替换”,属于直接在代码上进行修改,在预编译的时候这个符号就全部被替换成相应的值了,并不占用内存。但第二行代码中,如果修改了 PI
的值,就改动了对应内存空间的数值。
2.变量
2.1.变量与常变量
变量就是程序中定义的可以存入数据的存储空间,例如如下代码:
c
int a;
a = 2;
就是在内存中开辟了一个名为a的内存空间,再为其存入相应的值。示意图如下:
图:存储单元的示意图
变量必须先定义,后使用。没有被定义的变量是不允许赋值的,相反地,没有值的变量是没有意义的。对程序编译连接的时候,编译系统会为每一个变量名分配对应的内存地址。从中取值,实际上是通过变量名找到相应的内存地址,再从中读取数据。
C 99中允许使用常变量,例如:
c
const int a = 3;
这样就定义了一个整形变量 a
,但是变量存在期间它的值不允许被改变。这样我们称a是一个常变量。
2.2.标识符
在高级语言中,用来对变量、函数、类型等命名的字段统称为标识符(identifier),简单来说,标识符就是一段内存空间、一个变量、一个功能的名字。之前提到过的 printf
、PI
等都是标识符。
C语言规定,标识符只能由字母、数字、下划线构成,并且数字不能位于第一个字符。下面列出的都是合法标识符:
sun
average
_total
Student_1
下面列出的是不合法标识符:
M.J.Tom
3D86
$34
标识符在编译系统看来,同一个字母的大写与小写字母是两个不同的字母,Sum
与 sum
也是两个不同的变量名。按照人们的日常习惯,常常用小写字母来命名变量。
3.基本类型
在之前的例程中,我们发现,定义一个变量的时候要指定相应的变量类型。C语言中要求定义所有的变量都要指定相应的类型。这是为什么呢?
在数学运算中,数字是抽象存在的,并且允许模糊的计算存在,比如 10 / 3 = 3.333333……
,但是在计算机运算中,数值是具体存在于某一个内存单元的。并且,储存单元的大小是有限的,这样的话在计算机中就不存在无穷的概念。例如有如下程序:
c
printf("%d", 1.0 / 3.0);
输出结果将会是 0.333333
,只有6位小数,没有无限循环的小数,这就与"%d"
(整形)所表示的变量类型的存储能力有关。所谓变量类型,就是计算机对变量分配的存储空间的长度与存储形式。
3.1.整形 int
整形数据在内存中分配 2 个或 4 个字节,这与编译环境有关。Turbo C 2.0 为 int 型数据分配 2 个字节的存储空间,Visual C++ 为 int 型分配 4 个字节的存储空间。 int 型能容纳的数值范围为 -231~(231-1)(相应的存储方式的细节可以查阅参考资料[1]的3.2.3节(44页):《整形数据》)。
除了普通的 int 型数据之外,还有短整型(short int)和长整型(long int)数据类型,前者占 2 字节,后者占 4 个字节。由于实际编程需要,双长整型(long long int)新增进来。相信未来为了实际需要,还会加入更强大的变量类型。
常见的 int 型变量的储存空间和数值范围:
类型 | 字节数 | 数值范围 |
---|---|---|
int 基本整形 | 2 | -32678~32767 |
4 | -2147483648~2147483647 | |
unsigned int 无符号基本整形 | 2 | 0~65535 |
4 | 0~4294967295 | |
short 短整型 | 2 | -32768~32767 |
unsigned short 无符号短整型 | 2 | 0~65535 |
long 长整型 | 4 | -2147483648~2147483647 |
unsigned long 无符号长整型 | 4 | 0~4294967295 |
long long 双长型 | 8 | -9223372036854775808~9223372036854775807 |
unsigned long long 无符号双长整型 | 2 | 0~18446744073709551615 |
表:常见的 int 型变量的储存空间和数值范围
3.2.浮点型 float
浮点型数据是用来存储具有小数点的实数的。由于同一个小数用指数形式表示的时候有多种形式,例如 3.14159
可以表示为 3.14159e0
、0.34159e1
,它们代表同一个值。可以看到小数点是在数字之间浮动的,所以实数的指数形式称为浮点数。
规定小数点左边的数字为 0
,小数点右边的第一位数字不为 0
,这种小数的表示形式称为规范化的指数形式,例如 0.314159e1
。一个实数只有一个规范化的指数形式,在程序输出时以这种形式输出。
浮点数类型包括 float
单精度浮点型、double
双精度浮点型、long double
长精度浮点型。每种类型的存储方式各不相同,能表示的数值范围也各不相同,如下表:
类型 | 字节数 | 有效数字 | 数值范围(绝对值) |
---|---|---|---|
float | 4 | 6 | 0及1.2e-38~3.4e38 |
double | 8 | 15 | 0及2.3e-308~1.7e308 |
long double | 8 | 15 | 0及2.3e-308~1.7e308 |
6 | 19 | 0及3.4e-4932~1.1e4932 |
表:实型数据的有关情况
关于存储形式,可以参阅参考资料[1]的3.2.5节(50页):《浮点型数据》)
3.3.字符型 char
字符与字符代码不是任意写一个字符程序都能识别,有些时候为了表示一些系统字符集中没有的字符,只能使用系统的字符集。如圆周率 π
。目前大多数系统使用 ASCII 字符集,所有字符集大多都包含全部的 127 个基本字符。 (参见附录 1:ASCII 码对照表)
标准的字符型数据在系统中都用7位二进制数存放,例如数字 1
在 ASCII 码对照表中排在第 49 位,二进制码为 00110001
。在 C 中,规定用 1 个字节(8 位)存储一个字符,字节中的第一位为 0
。有关于字符型数据在计算机中的详细存储方式,可以参阅参考资料[1]的 3.2.4 节(48 页):《字符型数据》)
字符变量是用类型标识符 char
定义的变量,比如定义一个字符型变量:
c
char c = '?';
'?'
是字符型变量,它的 ASCII 编码为 63,所以在执行这个语句的时候,系统将整数 63
存入变量 c
。此外,字符型数据的存储空间和数值范围如下:
类型 | 字节数 | 数值范围 |
---|---|---|
signed char(有符号字符型) | 1 | -128~127 |
unsigned char(无符号字符型) | 1 | 0~255 |
表:字符型数据的存储空间和数值范围
此外,关于字符型变量用整数形式存储的相关知识将在《输入与输出》一节详细讲解。
4.其他类型
除了上面提到的基本类型之外,还有指针类型、函数类型、数组类型、结构体类型、共用体类型、枚举类型、空类型等。这些数据类型将在后面分别详细讲解。
二、运算符
1.算术运算符与算术表达式
几乎每一个程序都需要运算,否则程序就变得没有意义。要进行运算,就需要规定可以使用的运算符。C语言的运算符包含除了控制语句和输入输出以外的基本操作,基本的算术运算符如下表:
运算符与举例 | 名称 | 目 | 运算结果 |
---|---|---|---|
+a | 正号运算符 | 1 | a的值 |
-a | 负号运算符 | 1 | a的负值 |
a*b | 乘法运算符 | 2 | a和b的乘积 |
a/b | 除法运算符 | 2 | a除以b的商 |
a%b | 求余(取模)运算符 | 2 | a除以b的余数 |
a+b | 加法运算符 | 2 | a和b的和 |
a-b | 减法运算符 | 2 | a和b的差 |
a++++a | 自增运算符 | 1 | 使用a之后将a的值增加1 |
++a | 自增运算符 | 1 | 将a的值增加1再使用 |
a-- | 自减运算符 | 1 | 使用a之后将a的值减小1 |
--a | 自减运算符 | 1 | 将a的值减小1再使用 |
a?b:c | 选择运算符 | 3 | 判断a的真假,如果真执行b,假则执行c |
表:几种基本运算符
在这里需要一些相关说明:
由于键盘无法输入乘除号,所以以
*
和/
号代替。两个实数相除的结果是实数,两个整数相除是整数。但“取整”的舍去规则跟编译环境有关,有的编译器是向前取整,有的编译器是向后取整。例如
-5 / 3
的结果在有的编译器里是-1
,有的是-2
。取模操作(
%
)要求参加运算的两个对象都是整数,取模运算结果也是整数,例如5 % 3 = 2
。除%
之外的所有运算,参加运算的对象都可以是任何类型。自增(自减)运算符的运算与调用的先后顺序很重要,这一部分会在日后的《输入与输出》一节详细讲解。
有关于不同类型间的混合运算,请参阅参考资料[1]的 3.2.7 节(54页)。
在进行数值运算时,总会因为参加运算的两个数值类型不同而得不到正确的结果,这个时候可以对数据进行强制类型转换。强制类型转换符由一个括号加变量类型组成,例如:
c
(double)a
即为将变量 a
转换成 double
类型。一般形式为:
c
`(类型名)(表达式)`;
除了算术运算符以外,C语言还提供其他运算符,例如关系运算符、逻辑运算符、位运算符、逗号运算符、求字节数运算符等。相关知识读者可以自己查阅资料进行详细了解。
带有算术运算符组成的表达式叫做算术表达式,每一个算术表达式都有它的值。
2. 逻辑运算符与逻辑表达式
逻辑运算表达的是“关系”,逻辑表达式是一句可以判断真假的表达式。常用的逻辑运算符有三种:
运算符 | 含义 | 举例 | 说明 |
---|---|---|---|
&& | 逻辑与 | a && b | 只有在 a 与 b 都为真时为真 |
|| | 逻辑或 | a || b | a 和 b 之间有一个为真即为真 |
! | 逻辑非 | !a | !a 与 a 真值相反 |
表:常用的逻辑运算符
逻辑与和逻辑或为双目运算符,要求有两个对象参加运算。当参加运算的a和b为不同值的时候,逻辑运算得到的值也不相同:
a | b | !a | !b | a && b | a||b |
---|---|---|---|---|---|
T | T | F | F | T | T |
T | F | F | T | F | T |
F | T | T | F | F | T |
F | F | T | T | F | F |
表:逻辑运算的真值表
另外,逻辑运算有相应的运算顺序,最低为赋值(=
)运算,第二是 &&
和 ||
,第三是关系运算符,第四是算术运算符,最高是非(!
)运算。
三、语句
一个C语言程序由若干语句构成,语句大概可分为控制语句、函数调用语句、表达式语句、空语句。所有语句都用分号作为一个或一块语句的结束标志。
1.控制语句
控制语句在程序中完成一定的流程控制功能,相关内容将在第三部分:流程控制中详细讲解。C语言中共有9种控制结构,分别完成条件、循环、选择、跳转等功能。
2.函数调用语句
函数调用语句由函数名加括号加分号组成,例如我们在最开始见过的第一个程序:
c
printf("This is my NO.1 program");
3.表达式语句
表达式语句由一个表达式和一个分号构成,这里需要明确表达式与语句的区别。例如:
c
a = 3
a = 3;
其中,第一行是表达式,第二行是语句。这里体现到了分号的作用,分号是一个语句中不可缺少的一部分。函数调用语句在某种意义上也属于表达式语句,例如:
c
sin(x)
也是表达式语句的一种,同时它也是函数调用语句。在学习C语言时不要拘泥与概念,灵活学习语言,注重在应用上才是正确的学习方法。
4.空语句
下 面是一句空语句:
c
;
空语句仅由一个分号单独构成,它单独是一个语句,但它什么也不做。空语句常常用来做循环结构中的条件语句或循环体,表示这个循环体什么也不做。
5.语句块
用花括号{ }将一些语句包含在一起就组成了语句块,也叫做复合语句。这种结构常常用于选择结构和循环结构。这时需要程序连续执行一组语句。例如:
c
{
float pi = 3.14159, r = 2.5, area;
area = pi * r * r;
printf("area = %f", area);
}
四、输入与输出
输入与输出需要用到一个头文件 stdio.h
,在程序开头要用预处理指令将这个头文件包含进去:
c
#include<stdio.h>
这个头文件中其中包含的两个库函数:printf
与 scanf
,分别负责输出与输入。除此之外,C 语言中的标准输入输出函数还有其他几个函数,分别是:putchar
(输出字符)、getchar
(输入字符)、puts
(输出字符串)、gets
(输入字符串)。
首先要对输入输出做一些说明:
a. 所谓输入与输出是相对于计算机本身而言的,从计算机像输出设备(显示器、打印机等)输出数据称为输出,从输入设备(键盘、光盘、扫描仪等)像计算机输入数据称为输入。
b. C 语言本身不提供输出与输出函数,这两个函数是C语言标准库中提供的。
c. 在使用这两个函数时,一定要在程序开头用预处理指令
#include<stdio.h>
将这个头文件包含进去。
1.标准输出函数 printf
标准输出函数的一般格式为:
c
printf("格式控制", 输出表列);
格式控制是由双引号括起来的一个字符串,称为格式控制字符串,它包含两个部分,分别是格式声明(如 %d
,%f
)和普通字符,普通字符即为在输出时照原样输出的字符。“输出表列”是程序要输出的变量名或表达式,输出表列格式声明符一一对应。
由于输出函数是函数,所以格式控制和输出表列都是这个函数的参数。这样的话,标准输出函数的一般形式可以表示为:
c
printf(参数1 , 参数2 , 参数3 , …… , 参数n);
执行输出语句时,参数 2 ~参数 n 按照参数 1 中所指定的格式输出。参数 1 是必须要有的,参数 2 ~ 参数 n 是可选的。下面是一个标准输出函数的例子:
c
#include<stdio.h>
int main()
{
int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7;
printf("Number is %d%d%d%d%d%d%d\n", a, b, c, d, e, f, g);
return 0;
}
这个程序的执行结果为:
sh
Number is 1234567
需要提到的是,格式控制符决定输出表列中变量里存放的数据如何输出。前面说过,一切形式的数据在计算机中都是以二进制形式的编码进行存放,也就是说,在计算机的存储结构中,一切的数据的存储方式都是一样的。在输入时,通过格式控制将不同格式的数据转换成二进制编码进行存储,在输出时,将二进制编码按照需要的格式进行输出。上述内容可以用以下例程说明:
c
int a = 64;
printf("%d、%c", a, a);
上述输出语句输出结果是:
sh
64、@
C 语言中的格式控制符大概有以下几种:
格式控制符 | 说明 |
---|---|
%d、%i | 以带符号十进制形式输出整数 |
%o | 以八进制无符号形式输出 |
%x、%X | 以十六进制无符号形式输出整数,X、x分别代表输出时字母大小写 |
%u | 以无符号十进制形式输出整数 |
%c | 以字符形式输出 |
%s | 以字符串形式输出 |
%f | 以小数形式输出单、双精度数,隐含输出6位小数 |
%e、%E | 以指数形式输出实数,E、e代表输出时E(e)的大小写 |
表:进行输出时的常用格式控制符
需要特别提到的是,用 %f
输出时,可以控制输出宽度与保留的小数位数。例如 %5.2f
,表示输出时总共保留 5 位有效数字,2 位小数。例如:
c
float a = 3.14159;
printf("%2.1f", a);
这个输出语句的运行结果是:
sh
3.1
可以在输出函数中参数 1 的位置使用转义字符。由于格式控制符由百分号加一个字母组成,如果想输出百分号的话,需要用两个百分号(%%
)表示。例如:
c
printf("%f%%", 1.0 / 3);
输出结果为
sh
0.333333%
2.标准输入函数 scanf
标准输入函数的一般形式为:
c
scanf("格式控制", 地址表列);
scanf
函数的第一个参数为格式控制,与输出函数不同的是,用户在用 scanf
输入的时候,必须严格按照 scanf
函数中格式控制字符串中的格式输入。scanf
函数中的格式控制符与 printf
函数中大致相同。
需要注意到的问题是,scanf函数中参数 2 ~ 参数 n 是“地址表列”,而不是单单的变量名表列。例如:
c
scanf("%d%d%d", a, b, c);
这个语句是错误的,应该改为:
c
scanf("%d%d%d", &a, &b, &c);
“&
”符号称为“取地址符”,“&a
”代表变量 a
的地址。关于地址的相关内容请参阅第五部分:数组与指针。现在我们只需要知道应该为 scanf
函数提供你想为其输入值的地址即可。
3.字符数据的输入和输出
输入输出字符数据时用到的函数是 putchar
和 getchar
,这两个函数分别只能输出和接受一个字符。例如:
c
char a, b, c;
a = getchar();
b = getchar();
c = getchar();
putchar(a);
putchar(b);
putchar(c);
在 2 ~ 4 行分别为 a
、b
、c
输入一个字符,完成输入后会把 a
、b
、c
存放的字符原样输出。再例如:
c
int a = 65, b = 66, c = 67;
putchar(a);
putchar(b);
putchar(c);
程序的输出结果是:
sh
ABC
按照同样的方式,putchar
也可以输出转义字符。
五、练习题
设计一个程序,可以提示用户输入圆的半径,然后程序给出圆的面积
设计一个程序,提示用户输入二次函数的系数
a
、b
、c
,程序给出二次函数的解。已知二次函数求根公式:
- 思考下面每一行语句执行后
a
的值与printf
输出的值:
c
int a = 7;
printf("%d", a ++);
printf("%d", a --);
printf("%d", ++ a);
printf("%d", -- a);