第2章 数据类型与运算符

更新时间:2023-12-14 19:40:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

博学谷——让IT教学更简单,让IT学习更有效

第2章 数据类型与运算符

学习目标

? 理解进制和进制转换

? 牢记C语言中的关键字和标识符定义规则 ? 掌握不同数据类型间的转换 ? 学会使用运算符进行运用

通过上一章的学习,相信大家对C语言已经有了一个初步认知。接下来的第2章将针对C语言开发中必须要掌握的进制、常量、变量、运算符等基础知识进行讲解。

2.1 数据存储的原理

2.1.1 进制

进制是一种计数机制,它可以使用有限的数字符号代表所有的数值。对于任何一种进制——X进制,就表示某一位置上的数在运算时逢X进一位。实际生活中也有很多进制的应用场景,例如时间每过60秒,分钟就会加1,这就是六十进制。再比如对学生进行分组时,假设8人一组,可以让学生进行报数,报满8个数就多了一个小组,这就是八进制。接下来将针对C语言中的二进制、八进制和十六进制分别进行讲解。

1、二进制

在绝大多数计算机系统中,数据都是通过二进制的形式存在的。二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个符号来表示的数。它的基数为2,进位规则是“逢二进一”。

例如计算二进制算术“1+1”,因为个位数1是该位上最大的数,所以再加1后就会向十位进一,个位改为0,所以二进制算术“1+1”的结果是二进制数10。

为了帮助大家更好地理解二进制,接下来通过二进制和十进制的对比来描述二进制的表示方式,如表2-1所示。

表2-1

十进制 0 1 2 3 4 二进制 0000 0001 0010 0011 0100 十进制与二进制对照表

十进制 5 6 7 8 9 二进制 0101 0110 0111 1000 1001 从表2-1可以看出,当用二进制表示十进制数字2时,由于二进制的符号只有0和1,所以根据“逢二进一”的规则,需要向高位进一位,表示为0010。同理,使用二进制表示十进制数字4时,继续向高位进一位,表示为0100。

需要注意的是,十进制和二进制只是针对数字的不同表示方式,无论采用哪种方式表示一个数,它的值都是一样的。以十进制数7为例,十进制由符号“7”组成 ,而二进制由符号 “0”、“1”、“1”和“1”组成。

1

博学谷——让IT教学更简单,让IT学习更有效

2、八进制

八进制是一种“逢八进一”的进制,它由0~7这八个符号来描述。同样地,此处通过十进制和八进制的对比来描述八进制的表示方式,如表2-2所示。

表2-2

十进制 0 1 2 3 4 5 6 7 8 八进制 0 1 2 3 4 5 6 7 10 十进制与八进制对照表

十进制 9 10 11 12 13 14 15 16 17 八进制 11 12 13 14 15 16 17 20 21 从表2-2中可以看出,当使用八进制表示十进制数字8时,由于表示八进制的符号只有0~7,因此,根据逢八进一的规则,需要向高位进一位,表示为10。同理,使用八进制表示十进制数字16时,继续向高位进一位,表示为20。

3、十六进制

十六进制是一种“逢十六进一”的进制,它由0~9、A~F这十六个符号来描述。下面通过十进制和十六进制的对比来描述十六进制的表示方式,如表2-3所示。

表2-3

十进制 0 1 2 3 4 5 6 7 8 9 10 十六进制 0 1 2 3 4 5 6 7 8 9 A 十进制与十六进制对照表

十进制 17 18 19 20 21 22 23 24 25 26 27 十六进制 11 12 13 14 15 16 17 18 19 1A 1B

2

博学谷——让IT教学更简单,让IT学习更有效

11 12 13 14 15 16 B C D E F 10 28 29 30 31 32 33 1C 1D 1E 1F 20 21 从表2-3中可以看出,当使用十六进制表示十进制数字16时,由于表示十六进制的符号只有0~9、A~F,因此,根据逢“逢十六进一”的规则,需要向高位进一位,表示为10。同理,使用十六进制表示十进制数字32时,继续向高位进一位,表示为20。

2.1.2 进制转换

通过前面内容的学习,读者应该知道在计算机中一个数值可以用不同的进制形式来表示,但实际上,不管是哪种进制形式来表示,数值本身是不会发生变化的。因此,各种进制之间可以轻松地实现转换,下面就以十进制、二进制、八进制、十六进制为例来讲解如何实现进制转换。

一、十进制与二进制之间的转换

十进制与二进制之间的转换是最常见也是必须掌握的一种进制转换方式,下面针对十进制转二进制和二进制转十进制的方式分别进行讲解。 1、十进制转二进制

十进制转换成二进制可以采用除2取余的方式。也就是说将要转换的数,先除以2,获得商和余数,将商继续除以2,获得商和余数,此过程一直重复直到商为0。最后将所有得到的余数倒序排列,即可得到转换结果。

接下来就以十进制的6转换为二进制为例进行说明,其演算过程如图2-1所示。

除数 被除数 余数2226310图2-1 十进制转二进制

从图2-1中可以看出,十进制的6连续三次除以2后,得到的余数依次是:0、1、1。将所有余数倒序排列后为110,因此,十进制的6转换成二进制后的结果是110。

2、二进制转十进制

二进制转化成十进制要从右到左用二进制位上的每个数去乘以2的相应次方,例如,将最右边第一位的数乘以2的0次方,第二位的数乘以2的1次方,第n位的数乘以2的n-1次方,然后把所有乘的结果相加,得到的结果就是转换后的十进制。

例如,把一个二进制数0110 0100转换为10进制,转换方式如下:

0 * 20 + 0 * 21 + 1 * 22 + 0 * 23 + 0 * 24 + 1 * 25 + 1 * 26+ 0 * 27 = 100

011

由于0乘以多少都是0,所以上述表达式也可以简写为:

1 * 22 + 1 * 25 + 1 * 26 = 100

得到的结果100就是二进制数0110 0100转化后的十进制表现形式。 二、八进制与二进制之间的转换

3

博学谷——让IT教学更简单,让IT学习更有效

该类转换通常是二进制转换成八进制,在转换的过程中有一个技巧,就是将二进制数自右向左每三位分成一段(若不足三位,左边用0补齐),然后将二进制每段的三位转为八进制的一位,转换过程中数值的对应关系如表2-4所示。

表2-4

二进制和八进制数值对应表 二进制 000 001 010 011 100 101 110 111 八进制 0 1 2 3 4 5 6 7 接下来,就以将二进制数00101010为例来演示如何转为八进制,具体演算过程如下: (1)每三位分成一段,结果为:000 101 010; (2)将每段的数值分别查表替换,结果如下:

010 ? 2 101 ? 5 000 ? 0

(3)将替换的结果进行组合,组合后的八进制为0052(注意八进制必须以0开头)。

八进制转换成二进制的过程正好相反,只需将八进制数中每一位数转换成对应的三位二进制数即可。 三、十六进制与二进制之间的转换

将二进制转十六进制时,与转八进制类似,不同的是要将二进制数每四位分成一段(若不足4位用0补齐),查表转换即可。二进制转十六进制过程中数值的对应关系如表2-5所示。

表2-5 二进制 0000 0001 0010 0011 0100 0101 0110 0111 二进制和十六进制数值对应表 十六进制 0 1 2 3 4 5 6 7

二进制 1000 1001 1010 1011 1100 1101 1110 1111 十六进制 8 9 A B C D E F 接下来,二进制数01010110转为十六进制,具体步骤如下:

4

博学谷——让IT教学更简单,让IT学习更有效

(1)每四位分成一段,结果为:0101 0110; (2)将每段的数值分别查表替换,结果如下:

0110 ? 6 0101 ? 5

(3)将替换的结果进行组合,转换的结果为:0x56或0X56(注意十六进制必须以0x或者0X开头)。

上述讲解了二进制与其他进制的转换,除二进制外,其他进制之间的转换也很简单,只需将它们转换成二进制数,然后将二进制转为其他进制即可。

?多学一招:小数的二进制

十进制小数转换为二进制采用“乘2取整”规则。方法是用2乘以十进制小数部分,将其结果的整数部分去掉,用2乘以余下的小数部分,再去掉其结果的整数部分;如此继续下去,直到余下的小数部分为0或满足所要求的精度为准。最后将每次得到的整数部分(0或1)按先后顺序从前至后排列,即为小数对应的二进制。例如:将小数0.8125转换为二进制,转换过程如图2-2所示。

0.8125×21.6250????????取整数:1.6250×21.2500????????取整数:1.25×2 .50????????取整数:0×2 1.0????????取整数:1(0.8125)10=(0.1101)2顺序排列

图2-2 十进制小数转换为二进制

由图2-2所示十进制0.8125连续4次乘以2使得小数部分为0,所得整数依次为1、1、0、1,所以转换后结果为0.1101。

需要注意的是,有些十进制小数不一定能完全精确的转换为二进制,可以根据精度要求转换到小数点后某一位为止。

2.1.3 原码、反码、补码

一个数在计算机中的二进制存储形式,叫做这个数的机器数。机器数是带符号的,在计算机中用一个数的最高位存放符号,最高位为0表示是正数,最高位为1表示是负数。比如十进制数3的机器数是0000 0011;如果是-3,它的机器数就是1000 0011。

带符号位的机器数对应的真正数值称为机器数的真值。例如,0000 0011的真值是3,1000 0011的真值是-3。需要注意的是,机器数的真值和机器数转换成十进制数是不一样的,例如1000 0011转换成十进制数是131,但由于机器数的最高位存放符号,因此机器数的真值是-3。

机器数有原码、补码、反码这三种表现形式。计算机将一个数以二进制形式存储时,会将该数分别进行求原码、求反码、求补码这三步操作,最终将得到的机器数存入内存。

1、原码

原码就是符号位加上真值的绝对值。例如,1的原码为 0000 0001,-1的原码为1000 0001。 2、反码

反码的表示方法是:正数的反码是其原码,负数的反码是在其原码的基础上,符号位不变,其余各个位取反。例如1的原码是0000 0001,反码也是0000 0001,而-1的原码是1000 0001,反码则是1111 1110。

3、补码

补码的表示方法是:正数的补码就其原码,负数的补码是在其反码的基础上+1。例如1的原码是0000 0001,反码是0000 0001,补码也是0000 0001,而-1的原码是1000 0001,反码是1111 1110,补码则是1111 1111。

5

博学谷——让IT教学更简单,让IT学习更有效

最终一个数的补码就是该数在计算机中以二进制形式存放的机器数,例如数值1在计算机中的二进制形式为0000 0 01,而-1在计算机中的二进制形式为1111 1111。

2.1.4 原码、反码、补码的应用

通过前面的讲解,我们了解了原码、反码和补码,反码与补码的出现就是为了解决以下问题: ? 使符号位能与有效值部分一起参加运算,从而简化运算规则。 ? 使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。 接下来将通过两个案例来详细讲解原码、反码和补码在计算机减法运算中的应用。 1、机器数转换成真值

假设长度为2个字节的内存空间中存放着二进制数10010010 00111001,那么这块内存空间中存放的整数的数值是多少?

因为计算机中用一个数的最高位存放符号,最高位为0表示是正数,最高位为1表示是负数,所以该整数是负数。推导过程如下所示: (1)因为负数在内存中以补码的形式存放,所以二进制数10010010 00111001是该负数的补码; (2)因为负数的补码是在其反码的基础上+1,所以该负数的补码是二进制数10010010 00111000;

(3)因为负数的反码是在其原码的基础上,符号位不变,其余各个位取反,所以该负数的原码是二进制数11101101 11000111;

(4)无视最高位的数值,二进制数01101101 11000111转换成十进制数就是28103,再添加上符号,那么二进制数10010010 00111001的真

值就是-28103。 2、计算1-1的值

计算机计算1-1的值的步骤具体如下: (1)将算式1-1转换成算式1+(-1);

(2)将算式中的数字转换成原码,算式就成为:0000 0001 + 1000 0001; (3)原码转换成反码,算式就成为:0000 0001 + 1111 1110; (4)反码转换成补码,算式就成为:0000 0001 + 1111 1111; (5)对补码进行运算,得到结果:1 0000 0000;

(6)因为计算机使用8位二进制数,所以结果的第9位被舍去,所以最终结果是:0000 0000; (7)由于0000 0000是十进制数0的补码,所以计算机返回十进制数0。

2.2 关键字和标识符

2.2.1 关键字

所谓关键字是指在编程语言里事先定义好并赋予了特殊含义的单词,也称作保留字。关键字在程序中用于表示特殊含义,不能被随便用作变量名、函数名等,在C语言中,C89标准中共定义了32个关键字,具体如下:

auto double switch signed static

case

void while

return union

int enum const default

struct float goto

if

break

else

long

register typedef

short sizeof

char

extern

unsigned continue volatile do

for

6

博学谷——让IT教学更简单,让IT学习更有效

上面列举的关键字中,每个关键字都有特殊的作用,例如,int关键字用于声明一个整型的变量,sizeof关键字用于获取指定类型数据的长度,char关键字用于声明一个字符类型的变量。在本书后面的章节中将逐步对这些关键字进行讲解,这里只需了解即可。

2.2.2 标识符

在编程过程中,经常需要定义一些符号来标记一些名称,如变量名、方法名、参数名、数组名等,这些符号被称为标识符。在C语言中标识符的命名需要遵循一些规范,具体如下:

? 标识符只能由字母、数字和下划线组成。 ? 标识符不能以数字作为第一个字符。 ? 标识符不能使用关键字。

? 标识符区分大小写字母,如add、Add和ADD是不同的标识符。

? 尽量做到“见名知意”,以增加程序的可读性,如用age表示年龄,用length表示长度等。 ? 虽然ANSIC中没有规定标识符的长度,但建议标识符的长度不超过8个字符。

在上面的规范中,除了最后两条外,其他的命名规范都是必须要遵守的,否则程序就会出错。为了让读者对标识符的命名规范有更深刻地理解,接下来列举一些合法与不合法的标识符,具体如下:

下面是一些合法的标识符:

area DATE _name lesson_1

下面是一些不合法的标识符:

3a

// 标识符不能以数字作为第一个字符

ab.c // 标识符只能由字母、数字和下划线组成 long // 标识符不能使用关键字

abc#// 标识符只能由字母、数字和下划线组成

2.3 常量与变量

2.3.1 常量的概念

常量又称常数,是指在程序运行过程中其值不可改变的量。C语言中的常量可分为整型常量、实型常量、字符型常量、字符串常量和符号常量,下面将针对这些常量分别进行详细讲解。

1、整型常量

整型常量又称为整数。C语言中,整数可用三种形式表示,具体如下: ? 十进制整数:十进制整数没有前缀,其数码为0~9,如123、-456、0;

? 八进制整数:八进制整数必须以数字0开头,其数码为0~7,如0123表示八进制整数123、-011表示八进制整数-11;

? 十六进制整数:十六进制整数的前缀为0X或0x,其数码为0~9、A~F或a~f,如0x36C表示十六进制整数36C、-0x12d表示十六进制整数-12d。 2、 实型常量

实型也称为浮点型,实型常量也称为实数或浮点数,也就是在数学中用到的小数。C语言中,实型常量采用十进制表示,它有两种形式:十进制小数形式和指数形式,具体示例如下:

7

博学谷——让IT教学更简单,让IT学习更有效

? 十进制小数形式:由数字和小数点组成(注意:必须有小数点),如12.3、-45.6、1.0等;

? 指数形式:又称科学计数法,由于计算机输入输出时,无法表示上角或下角,所以规定以字母e或E表示以10为底的指数,如12.34e3(代表12.34×103)、-34.87e-2(代表34.87×10-2)、0.14E4(代表0.14×104)等。需要注意的是,“e”或“E”之前必须有数字,且“e”或“E”后面必须为整数,如不能写成e4、12e2.5等。 3、字符常量

有两种形式的字符常量:

? 普通字符:用单引号括起来的单个字符,如:'a'、'Z'、'3'、'?'、'#'。字符常量存储在内存中时,并不是存储字符(如a、Z、#等)本身,而是以其ASCII码存储的,例如字符'a'的ASCII码是97,因此在内存中以二进制形式存放97。需要注意的是,单引号只是界限符;字符常量只能是一个字符,不包括单引号,不能写成'ab'或'12';字符常量是区分大小写的,'a'和'A'是不同的字符常量。

? 转义字符:有些字符是无法用一般形式来表示的,我们可以用转义字符来表示,就是以字符'\\'开头的字符序列,例如'\\n'表示一个换行符、'\\t'表示一个制表符。常用转义字符如表2-6所示。

表2-6

转义字符 \\' \\\\\\\ \\a \\b \\f \\n \\r \\t \\v 转义字符及其作用

输出结果 输出此字符 输出此字符 输出此字符 产生声音或视觉信号 将当前位置后退一个字符 将当前位置移到下一页的开头 将当前位置移到下一行的开头 将当前位置移到本行的开头 将当前位置移到下一个tab位置 将当前位置移到下一个垂直制表对齐点 对应字符 单引号(') 双引号(\) 反斜线(\\) 警告(alert) 退格(backspace) 换页(form feed) 换行 回车(carriage return) 水平制表符 垂直制表符 1~3位八进制数表示的ASCII\\d、\\dd、\\ddd 码所代表的字符 1~2位十六进制数表示的\\xh、\\xhh ASCII码所代表的字符 与该八进制ASCII码对应的字符 与该十六进制ASCII码对应的字符 表2-6中倒数第2行是一个八进制数表示的ASCII字符,例如'\\101'代表八进制数101的ASCII字符,即十进制数65的ASCII字符'A';'\\012'代表八进制数12的ASCII字符,即十进制数10的ASCII字符'\\n'。表2-6中倒数第1行是一个十六进制数表示的ASCII字符,例如'\\x41'代表十六进制数41的ASCII字符,也就是十进制65的ASCII字符'A';'\\x20'代表十六进制数20的ASCII字符,即十进制数32的ASCII字符:空格字符。这两个方法可以表示任何可显示的字母字符、数字字符、专用字符、图形字符和控制字符。

计算机使用特定的整数编码来表示对应的字符。我们通常使用的英文字符编码是 ASCII(American Standard Code for Information Interchange 美国信息交换标准编码)。ASCII编码是一个标准,其内容规定了把英文字母、数字、标点、字符转换成计算机能识别的二进制数的规则,并且得到了广

8

博学谷——让IT教学更简单,让IT学习更有效 泛认可和遵守。具体请参考附录A中的ASCII码表。

? ASCII非打印控制字符:

ASCII表上的数字0-31分配给了控制字符,用于控制像打印机等一些外围设备(参详ASCII码表中0-31)。ASCII码中9代表Tab键,10代表换行键,下面将用一个案例来打印其效果,如例2-1所示。

例2-1

1 #include 2 void main() 3 { 4 5 6 }

printf(\printf(\

运行结果如图2-3所示。

图2-3 运行结果

如图2-3所示,a与b之间插入字符9,则a、b之间出现Tab制符表键的效果;c与d之间插入字符10,则c、d之间出现换行。 ? ASCII打印字符:

数字32-126 分配给了能在键盘上找到的字符,当查看或打印文档时就会出现。数字127代表 DELETE 命令(参详ASCII码表中32-127)。 4、字符串常量

字符串常量是用一对双引号括起来的字符序列,例如\、\、\等。字符串的长度等于字符串中包含的字符个数,例如,字符串\的长度为5个字符。

字符串常量与字符常量是不同的,它们之间主要的区别有: ? 字符型常量使用单引号定界,字符串常量使用双引号定界。 ? 字符型常量只能是单个字符,字符串常量可以包含一个或多个字符。

? 可以把一个字符型常量赋给一个字符型变量,但不能把一个字符串常量赋给一个字符串变量,C语言中没有相应的字符串变量,只能用字符数组存放字符串常量。 5、符号常量

C语言中也可以用一个标识符来表示一个常量,称为符号常量。符号常量在使用前必须先定义,其语法格式如下所示:

#define 标识符 常量

上述语法格式中,define是关键字,前面加符号“#”,表示这是一条预处理命令(预处理命令都以符号“#”开头),称为宏定义。 例如将圆周率用PI表示,可写成:

#define PI 3.14

该语句的功能是把标识符“PI”定义为常量3.14,定义后在程序中所有出现标识符“PI”的地方均用3.14代替PI。符号常量的标识符是用户自己定义的。

符号常量有以下特点:

? 习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。

9

博学谷——让IT教学更简单,让IT学习更有效

? 符号常量与变量不同,符号常量的值在其作用域内不能改变,也不能再被赋值。 ? 使用符号常量的好处是:含义清楚,并且能做到“一改全改”。

2.3.2 变量的定义

在程序运行期间,随时可能产生一些临时数据,应用程序会将这些数据保存在一些内存单元中,每个内存单元都用一个标识符来标识。这些内存单元我们称之为变量,定义的标识符就是变量名,内存单元中存储的数据就是变量的值。

接下来,通过一段代码来学习变量的定义,具体如下:

int x = 0, y; y = x+3;

上面的代码中,第一行代码的作用是定义了两个变量x和y,也就相当于分配了两块内存单元,在定义变量的同时为变量x分配了一个初始值0,而变量y没有分配初始值,变量x和y在内存中的状态如图2-4所示。

图2-4 x、y变量在内存中的状态

第二行代码的作用是为变量赋值,在执行第二行代码时,程序首先取出变量x的值,与3相加后,将结果赋值给变量y,此时变量x和y在内

存中的状态发生了变化,如图2-5所示。

图2-5 x、y变量在内存中的状态

从图2-4、图2-5以及上面的描述不难发现,变量实际上就是一个临时存放数据的地方。在程序中,可以将指定的数据存放到变量中,方便随时取出来再次进行使用。变量对于一段程序的运行是至关重要的,读者在后续的学习中会逐步地了解变量的作用。

2.3.3 变量的数据类型

在应用程序中,由于数据存储时所需要的容量各不相同,因此,为了区分不同的数据,需要将数据划分为不同的数据类型。C语言中的数据类型有很多种,具体如图2-6所示。

10

博学谷——让IT教学更简单,让IT学习更有效

短整型整型整型长整型基本数据类型实型(浮点型)字符型枚举型数据类型构造类型指针类型空类型图2-6 C语言的数据类型

从图2-6中可以看出,C语言中的数据类型可分为4种,分别是基本类型、构造类型、指针类型、空类型。接下来将针对基本数据类型进行详细地讲解,关于其他数据类型将在后面章节中讲解。

1、整型变量

在程序开发中,经常会遇到0、-100、1024等数字,这些数字都可称为整型。整型就是一个不包含小数部分的数。在C语言中,根据数值的取值范围,可以将整型定义为短整型(short int)、基本整型(int)和长整型(long int)。表2-7中列举了整数类型的长度及其取值范围。

表2-7

修饰符 数据类型 short [int] [signed] int long [int] short [int] unsigned int long [int] 占用空间 16位(2个字节) 32位(4个字节) 32位(4个字节) 16位(2个字节) 32位(4个字节) 32位(4个字节) 整数类型的长度及其取值范围

取值范围 -32768到32767 (-215 ~ 215-1) -2147483648到2147483647 (-231 ~ 231-1) -2147483648到2147483647 (-231 ~ 231-1) 0到65535 (0 ~ 216-1) 0到4294967295 (0 ~ 232-1) 0到4294967295 (0 ~ 232-1) 单精度型双精度型数组类型结构体类型共用体类型

从表2-7中可以看出,整数类型可分为short、int和long,这三种类型可以被signed和unsigned修饰。其中,被signed修饰的整数类型称为有符号的整数类型,被unsigned修饰的称为无符号的整数类型。它们之间最大的区别是无符号类型可以存放的正数范围比有符号类型中的范围大一倍,例如,int的取值范围是-231~231-1,而unsigned int的取值范围是0~232-1。默认情况下,整型数据都是有符号的,此时signed修饰符可以不用写。

需要注意的是,整型数据在内存中占的字节数与所选择的操作系统有关。虽然C语言标准中没有明确规定整型数据的长度,但long类型整数的长度不能短于int类型,int类型整数的长度不能短于short类型。

通过对表2-7的学习,我们了解了不同整数类型数据的取值范围,这些数据类型的极值在头文件stdlib.h中都有宏定义,可以将它们打印出来,如例2-2所示。

例2-2

1 #include 2 #include

11

博学谷——让IT教学更简单,让IT学习更有效

3 void main() 4 { 5 6 7 8 9 10 11 12 }

printf(\%d——%d\\n\printf(\

%d——%d\\n\

printf(\%d——%d\\n\

printf(\%u\\n\是无符号整数输出格式 printf(\system(\

%u\\n\

printf(\%u\\n\

运行结果如图2-7所示。

图2-7 运行结果

在图2-7中可以看出,将short、int、long的有符号与无符号极值都打印出来,与表2-6的一致。关于宏定义的相关知识,将在第8章详细讲解,这里同学们只需了解这些不同的宏定义对应着不同数据类型的极值即可。

?多学一招:计算机的数据单位

位(bit):电子计算机中最小的数据单位,每一位的状态只能是0或1。

字节(byte):8个二进制位构成1个字节,它是存储空间的基本计量单位。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。

KB:在一般的计量单位中,通常K表示1000,例如:1公里=1000米,经常被写为1km;1公斤=1000克,写为1kg。同样K在二进制中也有类似的含义,只是这时K表示1024,也就是2的10次方。1KB表示1K个Byte,也就是1024个字节。

MB:计量单位中的M(兆)是10的6次方,见到M自然想起要在该数值的后边续上六个0,即扩大一百万倍。在二进制中,MB也表示到了百万级的数量级,但1MB不正好等于1000000字节,而是1048576字节,即 1MB = 2E+20 Bytes = 1048576Bytes,也就是1024KB。

GB:比MB更大的单位,1GB=1024MB=220KB=230B。 TB:比GB更大的单位,1TB=1024GB。 2、实型变量

实型变量也可以称为浮点型变量,浮点型变量是用来存储小数数值的。在C语言中,浮点型变量分为两种:单精度浮点数(float)、双精度浮点数(double),但是double型变量所表示的浮点数比float型变量更精确。表2-8列举了两种不同浮点型数所占用的存储空间大小及取值范围。

表2-8

类型名 float double 占用空间 浮点类型长度及其取值范围

取值范围 32位(4个字节) 1.4E-45 ~ 3.4E+38,-1.4E-45 ~ -3.4E+38 64位(8个字节) 4.9E-324 ~ 1.7E+308, -4.9E-324 ~ -1.7E+308 表2-8中列出了两种浮点数类型变量所占的空间大小和取值范围。在取值范围中,E表示以10为底的指数,E后面的“+”号和“-”号代表正指数和负指数,例如,1.4E-45表示1.4*10-45。为了让读者更好地理解浮点型数据在内存中的存储方式,接下来以单精度浮点数3.14159为例进行详细讲解,如图2-8所示。

12

博学谷——让IT教学更简单,让IT学习更有效

图2-8 单精度浮点数存储方式

在图2-8中,浮点数包含符号位、小数位和指数位三部分,例如,小数3.14159在内存中的符号位为“+”,小数部分为.314159,指数位为1,连接在一起即为“+0.314159 * 101 = 3.14159”。

在C语言中,一个小数会被默认为double类型的值,在为一个float类型变量赋值时可以在后面加上字母“F”(或者小写“f”),double类型变量后加上“D”(或者小写“d”)来加以区分。但C语言在为变量赋值时可以自动转换类型,所以后缀可以省略,具体示例如下:

float f = 123.4f;

// 为一个float类型的变量赋值,后面可以加上字母f

// 为一个double类型的变量赋值,后面可以加上字母d

double d2 = 199.3d;

另外,在程序中也可以为一个浮点数类型变量赋予一个整数数值,示例如下:

float f = 100; double d = 100;

// 声明一个float类型的变量并赋整数值

// 声明一个double类型的变量并赋整数值

?脚下留心:float和double精确度

由于浮点型变量是由有限的存储单元组成的,因此只能提供有限的有效数字。在有效位以外的数字将不精确。双精度浮点数的有效位数更多,比单精度浮点数更能精确表达数据,它能提供15-16位有效位数,而单精度浮点数只能提供6-7位有效位数。例如,将1.12345678910111213赋给一个float型变量和dobule类型变量,其有效位数不同。如例2-3所示。

例2-3

1 #include 2 void main() 3 { 4 5 6 7 }

float f = 1.12345678910111213; double d = 1.12345678910111213;

printf(\是打印到小数点后17位

运行结果如图2-9所示。

图2-9 运行结果

从图2-9中可以看出,float类型的f变量精确到小数点后6位,double类型的d精确到小数点后15位,其后的数进行了四舍五入。 3、字符型变量

字符型变量用于存储一个单一字符,在C语言中用char表示,其中每个字符变量都会占用1个字节。在给字符型变量赋值时,需要用一对英文半角格式的单引号(' ')把字符括起来,例如,'A'的声明方式如下所示:

char ch = 'A'; // 为一个char类型的变量赋值字符'a'

上述代码中,将字符常量“A”放到字符变量ch中,实际上并不是把该字符本身放到变量的内存单元中去,而是将该字符对应的ASCII编码放到变量的存储单元中,例如:ASCII 使用编号 65 来对应大写字母“A”,因此变量ch存储的是整数65,而不是字母“A”本身。接下来通过一个案例来说明字符变量如何存放字符常量,如例2-4所示。

例2-4

1 #include 2 void main() 3 {

13

博学谷——让IT教学更简单,让IT学习更有效

4 char ch1 = 'A'; 5 char ch2 = 65; 6 printf(\ 7 printf(\ 8 }

运行结果如图2-10所示。

图2-10 ASCII字符

例2-4中,定义了两个char类型变量,分别赋值为字符“A”和数字65,然后通过printf()函数把两个变量的内容以字符形式打印到屏幕上。从图2-10中可以看出,两个变量输出的结果是一样的,这说明对于字符型来说,A和65其实没什么区别。严格来说,字符类型也是整型类型。

需要注意的是,除了可以直接从键盘上输入的字符(如英文字母,标点符号,数字,数学运算符等)以外,还有一些字符是无法用键盘直接输入的,比如,“回车”,此时需要采用一种新的定义方式——转义字符,它以反斜杠(\\)开头,随后接特定的字符。

接下来,通过一个具体的案例来演示转义符的用法,如例2-5所示。 例2-5

1 #include 2 void main() 3 {

4 char ch1 = 'A'; 5 char ch2 = '\\n'; 6 char ch3 = 'B'; 7 char ch4 = '\\\\'; 8 printf(\ 9 printf(\ 10 printf(\ 11 printf(\ 12 printf(\ 13 }

运行结果如图2-11所示。

图2-11 ASCII字符的输出结果

在例2-5中定义了四个字符型变量,其中ch2被赋值为转义字符“\\n”即换行符,ch4被赋值为转义字符“\\\\”即反斜杠。从第8行到第11行按照顺序分别将五个变量的值输出到屏幕上,我们会发现输出字符A之后另起一行输出字符B,转义字符“\\n”的作用就是控制输出结果另起一行;字符B后输出的是字符“\\”。第12行为了使输出结果的格式清晰一些又输出了一个换行符,防止程序结束后命令行提示符紧跟在输出结果的后面。

4、枚举类型变量

在日常生活中有许多对象的值是有限的,可以一一列举出来。例如一个星期内只有七天、一年只有十二个月等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。为此,C语言提供了一种称为“枚举”的类型。枚举类型就是其值可以被一一列举出来,并且变量的取值不能超过定义的范围。

枚举类型的声明方式比较特殊,具体格式如下:

enum 枚举名 {标识符1 = 整型常量1, 标识符2 = 整型常量2, ...};

在上述代码中,enum表示声明枚举的关键字,枚举名表示枚举对象的名称。为了让读者更好的理解枚举类型的使用,接下来通过定义一个枚举类型来进行详细讲解。示例如下:

14

博学谷——让IT教学更简单,让IT学习更有效

enum month { JAN=1, FEB=2, MAR=3, APR=4, MAY=5, JUN=6, JUL=7, AUG=8, SEP=9, OCT=10, NOV=11, DEC=12 };

上面的代码中,声明了一个枚举类型对象month,然后就可以使用此类型来定义变量。接下来,通过一个具体案例来学习枚举的具体用法,如例 2-6 所示。

例2-6

1 #include

2 enum month {JAN=1, FEB=2, MAR=3, APR=4, MAY=5, JUN=6, 3 JUL=7, AUG=8, SEP=9, OCT=10, NOV=11, DEC=12}; 4 void main() 5 {

6 enum month lastmonth, thismonth, nextmonth; 7 lastmonth = APR; 8 thismonth = MAY; 9 nextmonth = JUN;

10 printf(\ 11 }

运行结果如图2-12所示。

图2-12 运行结果

在例2-6中,第2行代码定义了一个枚举类型,其中,枚举名month是一个标识符,大括号中的内容被称为枚举值表;枚举值表内的标识符JAN、FEB、MAR等被称作枚举元素,枚举元素是被命名的整型常量,枚举值表中罗列出所有可用值,枚举成员对应的值是枚举值,枚举类型的定义以分号结束。第6行定义了三个枚举变量,在后面的代码中分别进行了赋值,最后依次将这三个枚举变量的值输出到显示器。可以看出输出结果为枚举元素对应的值。

需要注意的是,枚举值是常量,不是变量,在程序中不能赋值。例如在主函数中,对MAR再次赋值是错误的。

?多学一招:枚举变量的快速定义

在枚举中规定,如果不给标识符指定具体的值,会默认该标识符的值等于前一标识符的值加1。因此可以将上面的定义简化成:

enum month{JAN=1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC};

如果不指定第一个标识符对应的常量,则它的默认值是0,如例2-7所示。 例2-7

1 #include 2 // 定义一组常量

3 enum Constants {C1, C2, C3 = 4, C4, C5 = 3, C6, C7, C8 = '0', C9}; 4 void main() 5 { 6 7 }

printf(\

运行结果如图2-13所示。

图2-13 运行结果

在例2-7中,首先用枚举定义了一组常量C1到C9,并向显示器输出常量C1的数值。作为读者,在编译和运行该例程之前,最好先试着猜测C1到C9这九个常量的数值。

15

博学谷——让IT教学更简单,让IT学习更有效

2.3.4 数据类型的转换

在C语言程序中,经常需要对不同类型的数据进行运算,为了解决数据类型不一致的问题,需要对数据的类型进行转换。例如一个浮点数和一个整数相加,必须先将两个数转换成同一类型。C语言程序中的类型转换可分为隐式和强制类型转换两种,具体如下:

1、隐式类型转换

隐式类型转换又称为自动类型转换,隐式类型转换可分为三种:算术转换、赋值转换和输出转换。 (1)算术转换:

进行算术运算(加、减、乘、除、取余以及符号运算)时,不同类型数招必须转换成同一类型的数据才能运算。算术转换原则为: ① 对于所有比int小的类型,包括char、unsigned char、short、unsigned short,首先会提升为int类型。 ② 在进行运算时,以表达式中最长类型为主,将其他类型位据均转换成该类型,如:

? 若运算数中有double型或float型,则其他类型数据均转换成double类型进行运算。 ? 若运算数中最长的类型为long型.则其他类型数均转换成long型数。 ? 若运算数中最长类型为int型,则char型也转换成int型进行运算。 特别注意的是有符号和无符号之间的转换:

long与unsigned int在32位机器上都是4字节,所以均转换为unsigned long。signed与unsigned int,signed会转换为unsigned。如果int恰好为负数,其结果为对unsigned取值求模的结果。比如将-1赋给8位的unsigned char,那么结果就是255(-1对256取模后的值)。unsigned char取值范围0~255。

(2)赋值转换:

进行赋值操作时,赋值运算符右边的数据类型必须转换成赋值号左边的类型,若右边的数据类型的长度大于左边,则要进行截断或舍入操作。 下面用实例进行说明:

char ch; int i, result; float f; double d;

result=ch/i+(f*d-i);

① 首先计算“ch/i”,因为变量i是int型,所以变量ch转换为int型,“ch/i”返回int型结果。

② 接着计算“f*d-i”,由于最长型为double型(变量d),故变量f转换为double型,变量i转换为double型,“f*d-i”的结果也转换为double

型。

③ “ch/i”和“f*d-i”进行加运算,由于“f*d-i”的返回值为double型,故“ch/i”的返回值也转换为double型,“ch/i+(f*d-i)”的返回值是

double型。

④ 由于result为int型,故“ch/i+(f*d-i)”的返回值由double型转换为int型,即进行截断与舍入,最后取值为整型。 (3)输出转换:

在程序中将数据用printf()函数以指定格式输出时,当要输出的数据类型与输出格式不符时,便自动进行类型转换,如一个整(int)型数据用字符(char)型格式(%c)输出时,相当于将int型转换成char型数据输出;一个字符(char)型数据用整(int)型格式输出时,相当于将char型转换成int型输出。

需要注意的是,较长型数据转换成短型数据输出时,其值不能超出短型数据允许的值范围,否则转换时将出错,例如:

16

博学谷——让IT教学更简单,让IT学习更有效

int a=321; printf(\

运行结果为“A”,因为char型允许的最大值为255,321超出此值,故结果取以256为模的余数,即进行取余运算:321%6=65,而65正是字符“A”的ASCII码。

输出的数据类型与输出格式不符时常常发生错误,如:

int d=9; printf(\

float c=3.2; printf(\

2、显式类型转换

显式类型转换又称为强制类型转换,所谓显式类型转换指的是使用强制类型转换运算符,将一个变量或表达式转化成所需的类型,这种类型转换有可能会造成数据的精度丢失,其基本语法格式如下所示:

(类型名)(表达式)

在上述格式中,类型名和表达式都需要用括号括起来,具体示例如下:

double x; int y;

(double)((int)x + y) // 将表达式x+y的值转换成double类型 (int)x+y

// 将变量x的值转换成int后,再与y相加

上述讲解的两种类型转换,看起来很简单,但在使用时有许多细节需要注意,具体如下: (1)浮点型与整型

将浮点数(单双精度)转换为整数时,将舍弃浮点数的小数部分,只保留整数部分。将整型值赋给浮点型变量,数值不变,只将形式改为浮点形式,即小数点后带若干个0。需要注意的是,赋值时的类型转换实际上是强制的。 (2)单、双精度浮点型

由于C语言中的浮点值总是用双精度表示的,所以float 型数据参与运算时只需要在尾部加0延长为double型数据。double型数据转换为float型时,会造成数据精度丢失,有效位以外的数据将会进行四舍五入。 (3)char型与int型

将int型数值赋给char型变量时,只保留其最低8位,高位部分舍弃。

将char型数值赋给int型变量时, 一些编译程序不管其值大小都作正数处理,而另一些编译程序在转换时会根据char型数据值的大小进行判断,若值大于127,就作为负数处理。对于使用者来讲,如果原来char型数据取正值,转换后仍为正值。如果原来char型值可正可负,则转换后也仍然保持原值,只是数据的内部表示形式有所不同。 (4)int型与long型

long型数据赋给int型变量时,将低16位值送给int型变量,而将高16位截断舍弃。(这里假定int型占两个字节)。将int型数据送给long型变量时,其外部值保持不变,而内部形式有所改变。 (5)无符号整数

将一个unsigned型数据赋给一个长度相同的整型变量时(如:unsigned→int、unsigned long→long,unsigned short→short),内部的存储方式不变,但外部值却可能改变。

17

博学谷——让IT教学更简单,让IT学习更有效

将一个非unsigned整型数据赋给一个长度相同的unsigned型变量时,内部存储形式不变,但外部表示时总是无符号的。 为了帮助大家更好地学习类型转换,接下来通过一个案例进行一些简单的算术,具体如例2-8所示。 例2-8

1 #include 2 void main() 3 { 4 5 6 7 8 9 }

float a = 11, b; int c = 2, d; b = a / c; d = (int)b;

printf(\

运行结果如图2-14所示。

图2-14 运行结果

从图2-14中可以看出,变量b和d的值并不一样。程序在执行“a / c”时,因为变量a的类型是float型,而变量c的类型是int型,所以先将c的类型转换成float型,再进行计算,最终返回的结果是float类型的数值5.5,最后把该结果赋值给变量b;程序执行“d = (int)b”语句时,因为变量b的类型是float型,所以在被强制转换成int型时,将小数部分舍去,返回int型值5,最后把该结果赋值给变量d。

2.4 运算符

运算符是编程语言中不可或缺的一部分,用于对一个或多个值(表达式)进行运算。本节将针对C语言中的常见运算符进行详细地讲解。

2.4.1 运算符与表达式

在应用程序中,经常会对数据进行运算,为此,C语言提供了多种类型的运算符,即专门用于告诉程序执行特定运算或逻辑操作的符号。根据运算符的作用,可以将C语言中常见的运算符分为以下几类,具体如表2-9所示。

表2-9

运算符类型 算术运算符 赋值运算符 关系运算符 逻辑运算符 三目运算符 逗号运算符 位运算符 sizeof运算符 用于处理四则运算 用于将表达式的值赋给变量 用于表达式的比较,并返回一个真值或假值 用于根据表达式的值返回真值或假值 用于根据表达式的值执行相应的语句 用于连接并执行若干表达式,并返回最后一个表达式的值 用于处理数据的位运算 用于求字节数长度 常见的运算符类型及其作用

作用 表2-9列举了C语言中常用的运算符类型,并且每种类型运算符的作用都不同。运算符是用来操作数据的,因此,这些数据也被称为操作数,使用运算符将操作数连接而成的式子称为表达式。表达式具有如下特点:

18

博学谷——让IT教学更简单,让IT学习更有效

? 常量和变量都是表达式,例如,常量3.14、变量i。

? 运算符的类型对应表达式的类型,例如,算术运算符对应算术表达式。 ? 每一个表达式都有自己的值,即表达式都有运算结果。

2.4.2 算术运算符

在数学运算中最常见的就是加减乘除四则运算。C语言中的算术运算符就是用来处理四则运算的符号,这是最简单、最常用的运算符号。表2-10列出了C语言中的算术运算符及其用法。

表2-10 算术运算符

运算符 + - + - * / % ++ ++ -- -- 运算 正号 负号 加 减 乘 除 取模(即算术中的求余数) 自增(前) 自增(后) 自减(前) 自减(后) 范例 +3 b=4;-b; 5+5 6-4 3*4 5/5 7%5 a=2;b=++a; a=2;b=a++; a=2;b=--a; a=2;b=a--; 结果 3 -4 10 2 12 1 2 a=3;b=3; a=3;b=2; a=1;b=1; a=1;b=2; 算术运算符看上去都比较简单,也很容易理解,但在实际使用时还有很多需要注意的问题,接下来就针对其中比较重要的几点进行详细地讲解,具体如下:

1、进行四则混合运算时,运算顺序遵循数学中“先乘除后加减”的原则。例如,计算表达式“1+2*3”时,计算机先计算“2*3”的值,返回6,再计算“1+6”的值,返回7。

2、在进行自增(++)和自减(--)的运算时,如果运算符(++或--)放在变量的前面则是先进行自增或自减运算,再参与其他运算。反之,如果运算符放在操作数的后面则是先进行运算,再进行自增或自减。

请仔细阅读下面的代码块,思考运行的结果。

j=3; k=++j; printf(\j=3; k=j++; printf(\

第一行代码的运行结果是:4 4,因为++j先使变量j自增1,然后再将自增后的值赋值给k,即4;第二行代码的运行结果是:4 3,因为j++是先将j值赋值给k,再进行自增运算,即4;

3、在进行除法运算时,当除数和被除数都为整数时,得到的结果也是一个整数。如果除法运算有浮点数参与运算,系统会将整数数据隐形类型转换为浮点类型,最终得到的结果会是一个浮点数。例如,“2510/1000”属于整数之间相除,会忽略小数部分,得到的结果是2,而“2.5/10”的实际结果为0.25。

19

博学谷——让IT教学更简单,让IT学习更有效

请思考一下下面表达式的结果:

printf(\

结果为3000。由于表达式的执行顺序是从左到右,且三个常量是整型常量,所以先执行除法运算3500/1000,得到结果为3,然后再乘以1000,最终得到的结果自然就是3000。

4、取模运算在程序设计中都有着广泛的应用,例如判断奇偶数的方法就是求一个数字除以2的余数是1还是0。在进行取模运算时,运算结果的正负取决于被模数(运算符%左边的数)的符号,与模数(运算符%右边的数)的符号无关,如:(-5)%3=-2,而5%(-3)=2。

为了帮助大家更好地学习算术运算符,接下来通过一个案例来互换一个两位整数的个位和十位,具体如例2-9所示。 例2-9

1 #include 2 void main() 3 { 4 5 6 7 8 }

int num = 52, i, j; i = num / 10; j = num % 10;

// 这里定义一个整数52,52就是用于互换个位和十位的整数 // i用于存储整数52的十位,即5 // j用于存储整数52的个位,即2

printf(\将j作为十位,i作为个位,组成新的整数并输出

运行结果如图2-15所示。

图2-15 运行结果

从图2-15中可以看出,互换个位和十位后组成的新的整数打印了出来,由此可见,使用算术运算符可以很方便地对数据进行运算。

2.4.3 赋值运算符

赋值运算符的作用就是将常量、变量或表达式的值赋给某一个变量。表2-11列举了C语言中的赋值运算符及其用法。

表2-11 赋值运算符

运算符 = += -= 运算 赋值 加等于 减等于 乘等于 除等于 模等于 范例 a=3;b=2; a=3;b=2;a+=b; a=3;b=2;a-=b; a=3;b=2;a*=b; a=3;b=2;a/=b; a=3;b=2;a%=b; 结果 a=3;b=2; a=5;b=2; a=1;b=2; a=6;b=2; a=1;b=2; a=1;b=2; *= /= %= 在表2-11中,“=”的作用不是表示相等关系,而是赋值运算符,即将等号右侧的值赋给等号左侧的变量。在赋值运算符的使用中,需要注意以下几个问题:

1、在C语言中可以通过一条赋值语句对多个变量进行赋值,具体示例如下:

int x, y, z; x = y = z = 5;

// 为三个变量同时赋值

在上述代码中,一条赋值语句可以同时为变量x、y、z赋值,这是由于赋值运算符的结合性为“从右向左”,即先将5赋值给变量z,然后再把变量z的值赋值给变量y,最后把变量y的值赋值变量x,表达式赋值完成。需要注意的是,下面的这种写法在C语言中是不可取的。

20

博学谷——让IT教学更简单,让IT学习更有效

int x = y = z = 5;

// 这样写是错误的

2、在表2-11中,除了“=”,其他的都是特殊的赋值运算符,接下来以“+=”为例,学习特殊赋值运算符的用法,示例代码如下:

int x=2; x+=3;

上述代码中,执行代码x += 3后,x的值为5。这是因为在表达式x+=3中的执行过程为: (1)将x的值和3的执行相加。 (2)将相加的结果赋值给变量x。

所以,表达式x+=3就相当于x = x + 3,先进行相加运算,再进行赋值。-=、*=、/=、%=赋值运算符都可依此类推。 为了帮助大家更好地学习赋值运算符,接下来,通过一个案例来了解赋值运算符的执行流程,具体如例2-10所示。 例2-10

1 #include 2 void main() 3 { 4 5 6 7 }

int num = 6;

num -= num *= num + num; printf(\

运行结果如图2-16所示。

图2-16 运行结果

从图2-16中可以看出,运算后变量num的值被打印出来了。程序在处理到“num -= num *= num + num”时,先处理“num + num”,返回12;然后运算“num *= 12”,num赋值为72,返回72;最后运算“num -= 72”,num赋值为0,最终输出num的值为0。

?多学一招:运算符的结合性

运算符的结合性指同一优先级的运算符在表达式中操作的结合方向,即当一个运算对象两侧运算符的优先级别相同时, 运算对象与运算符的结合顺序。大多数运算符结合方向是“自左至右”。示代码如下:

a-b+c;

上述代码中表达式“a- b+c”,b两侧有-和+两种运算符,其优先级相同,按先左后右的结合方向,b先与减号结合,执行a - b的运算, 然后再执行加c的运算。除了自左至右的结合性外,C语言有一些运算符遵循的是自右向左的结合性,例如三目运算符、赋值运算等。以赋值运算符为例,具体代码如下所示:

a=b+c;

上述代码中,先执行b+c,然后将执行结果赋值给a。

2.4.4 关系运算符

关系运算符用于对两个数值或变量进行比较,其结果是一个逻辑值(“真”或“假”),如“5>3”,其值为“真”。C语言的关系运算中,“真”用数字“1”来表示,“假”用数字“0”来表示。表2-12列出了C语言中的关系运算符及其用法。

表2-12 关系运算符

运算符 ==

运算 相等于 范例 4 == 3 结果 0 21

博学谷——让IT教学更简单,让IT学习更有效

!= < > <= >= 请思考一下下面表达式的结果:

printf(\

不等于 小于 大于 小于等于 大于等于 4 != 3 4 < 3 4 > 3 4 <= 3 4 >= 3 1 0 1 0 1 上述表达式结果为0。该表达式首先计算5 <= 4,返回0,再计算0 > 1,返回0,最终得到的结果自然就是0。需要注意的是,在使用关系运算符时,不能将关系运算符“==”误写成赋值运算符“=”。

2.4.5 逻辑运算符

逻辑运算符用于判断数据的真假,其结果仍为“真”或“假”。表2-13列举了C语言中的逻辑运算符及其范例。

表2-13 逻辑运算符

运算符 运算 范例 结果 如果a为假,则!a为真 ! 非 !a 如果a为真,则!a为假 && 与 a&&b 如果a和b都为真,则结果为真否则为假 如果a和b有一个或一个以上为真, || 或 a || b 则结果为真,二者都为假,结果为假 当使用逻辑运算符时,有一些细节需要注意,具体如下: 1、逻辑表达式中可以包含多个逻辑运算符,例如,!a||a>b 2、三种逻辑运算符的优先级从高到低依次为:!、&&、||

3、运算符“&&”表示与操作,当且仅当运算符两边的表达式结果都为真时,其结果才为真,否则结果为假。如果左边为假,那么右边表达式是不会进行运算的,具体示例如下:

a+b

若a=5,b=4,c=3,d=3,由于a+b的结果大于c,表达式a+b

4、运算符“||”表示或操作,当且仅当运算符两边的表达式结果都为假时,其结果为假。同“&&”运算符类似,如果运算符“||”左边操作数的结果为真,右边表达式是不会进行运算的,具体示例如下:

a+b

若a=1,b=2,c=4,d=5,由于a+b的结果小于c,表达式a+b

为了帮助大家更好地学习逻辑运算符,接下来,通过一个案例来了解逻辑运算符的执行流程,具体如例2-11所示。 例2-11

1 #include

22

博学谷——让IT教学更简单,让IT学习更有效

2 void main() 3 { 4 5 6 }

int a = 5, b = 6, c = 7, d = 8, m = 2, n = 2;

printf(\

运行结果如图2-17所示。

图2-17 运行结果

从图2-17中可以看出,运算后逻辑运算的值、变量m的值和变量n的值被打印出来了。该程序运行到逻辑运算符时,首先执行m=a>b,m赋值为0,返回m的值;然后逻辑运算符&&停止接下来的运算并返回0。此时逻辑运算返回0,m赋值为0,n的值不变,依然为2。

2.4.6 三目运算符

如果在条件语句中,只执行单个的赋值运算符时,通常可以使用“?:”,它是一个三目运算符,即有三个参与运算的表达式。 三目运算符的语法格式如下所示:

表达式1 ? 表达式2 : 表达式3

上面的表达式中,先求解表达式1,若其值为真(非0)则将表达式2的值作为整个表达式的取值,否则(表达式1的值为0)将表达式3的值作为整个表达式的取值。

为了帮助大家更好地学习,接下来通过一个案例来了解三目运算符的使用,具体如例2-12所示。 例2-12

1 #include 2 void main() 3 { 4 5 6 7 }

int a = 6, b = 3, result; result = a > b ? a * b : a + b; printf(\

运行结果如图2-18所示。

图2-18 运行结果

从图2-18中可以看出,执行后变量result的值被打印出来了。该程序首先执行a>b,返回逻辑值真,接着执行符号“?”后面的语句,返回a * b的值并将其赋给变量result,此时result赋值为18并被打印出来。

注意:

1、条件运算符“?”和“:”是一对运算符,不能分开单独使用。

2、条件运算符的结合方向自右向左,例如a>b?a:c>d?c:d应该理解为a>b?a:(c>d?c:d),这也是三目运算符的嵌套情形,即其中的表达式3又是一个条件表达式。

2.4.7 逗号运算符

在C语言中,多个表达式可以写在一起,并用逗号分开。逗号运算符就是分割这些连在一起的表达式的运算符,其中用逗号分开的表达式的值先分

23

博学谷——让IT教学更简单,让IT学习更有效

别进行运算,最终返回最后一个表达式的值。逗号运算符的表达式如下所示:

(表达式1, 表达式2, 表达式3, ..., 表达式n);

程序将依次执行表达式1、表达式2、表达式3直至表达式n,并且最终返回表达式n的值。

为了帮助大家更好地理解逗号运算符,接下来,通过一个案例来了解逗号运算符的使用,具体如例2-13所示。 例2-13

1 #include 2 void main() 3 { 4 5 6 7 }

int a = 5, b;

b = (a++, 9 - 2, a + 3); printf(\

运行结果如图2-19所示。

图2-19 运行结果

从图2-19中可以看出,变量b的值被打印出来了。例2-13在程序执行时,首先计算a++,此时a自增为6;然后计算9 - 2;最后计算a + 3,结果为9,此时这是最后一个表达式,逗号运算符返回该表达式的值并赋给变量b。最终该程序打印出变量b的值为9。

2.4.8 位运算符

位运算符是针对二进制数的每个二进制位进行运算的符号,它是专门针对数字0和1进行操作的。C语言中的位运算符及其范例如表2-1414所示。

表2-14 位运算符

运算符 运算 范例 0 & 0 0 & 1 & 按位与 1 & 1 1 & 0 0 | 0 0 | 1 | 按位或 1 | 1 1 | 0 ~0 ~ 取反 ~1 0 ^ 0 0 ^ 1 ^ 按位异或 1 ^ 1 1 ^ 0

0 1 24

0 0 1 1 1 1 1 0 0 1 结果 0 0 博学谷——让IT教学更简单,让IT学习更有效

00000010<<2 << 左移 10010011<<2 01100010>>2 >> 右移 11100010>>2 11111000 01001100 00011000 00001000 接下来通过一些具体示例,对表2-14中描述的位运算符进行详细介绍,为了方便描述,下面的运算都是针对byte类型的数,也就是1个字节大小的数。

1、 与运算符“&”是将参与运算的两个二进制数进行“与”运算,如果两个二进制位都为1,则该位的运算结果为1,否则为0。

例如将6和11进行与运算,6对应的二进制数为00000110,11对应的二进制数为00001011,具体演算过程如下所示:

&

00000110 00001011

—————————

00000010

运算结果为00000010,对应数值2。

2、 位运算符“|”是将参与运算的两个二进制数进行“或”运算,如果二进制位上有一个值为1,则该位的运行结果为1,否则为0。具体示例如下:

例如将6与11进行或运算,具体演算过程如下:

|

00000110 00001011

—————————

00001111

运算结果为00001111,对应数值15。

3、 位运算符“~”只针对一个操作数进行操作,如果二进制位是0,则取反值为1;如果是1,则取反值为0。

例如将6进行取反运算,具体演算过程如下: ~ 00000110

—————————

11111001

运算结果为11111001,对应数值-7。

4、 位运算符“^”是将参与运算的两个二进制数进行“异或”运算,如果二进制位相同,则值为0,否则为1。

例如将6与11进行异或运算,具体演算过程如下:

^

00000110

00001011

—————————

00001101

运算结果为00001101,对应数值13。

5、 位运算符“<<”就是将操作数所有二进制位向左移动一位。运算时,右边的空位补0。左边移走的部分舍去。

例如一个byte类型的数字11用二进制表示为00001011,将它左移一位,具体演算过程如下:

00001011 <<1 ————————— 00010110

运算结果为00010110,对应数值22。

25

博学谷——让IT教学更简单,让IT学习更有效

6、 位运算符“>>”就是将操作数所有二进制位向右移动一位。运算时,左边的空位根据原数的符号位补0或者1(原来是负数就补1,是正数就

补0)。

例如一个byte的数字11用二进制表示为00001011,将它右移一位,具体演算过程如下:

00001011 >>1 —————————

00000101

运算结果为00000101,对应数值5。

为了帮助大家更好地学习位运算符,接下来通过一个案例来了解位运算符的使用,具体如例2-14所示。 例2-14

1 #include 2 void main() 3 { 4 5 6 }

int a = 13, b = 6;

printf(\

运行结果如图2-20所示。

图2-20 运行结果

从图2-20中可以看出,位运算的结果被打印出来了。变量a的值是13,转换成二进制数是1101;变量b的值是6,转换成二进制数是110。语句a&b的演算过程如下所示:

0110

—————————

0100

运算结果为0100,转换成十进制数是4。语句a^b的演算过程如下所示:

1101 ^

0110

—————————

1011

运算结果为1011,转换成十进制数是13。

&

1101

?多学一招:通过位运算取模

假设有个char型变量,现将其模4,求返回值。

首先我们看出,4的二进制数是100,所以将变量模4就是保持这个变量的二进制数的前2位不变,其余位变为0。

例如当变量的值为27时,此时该变量的二进制数是00011011,若想保证前2位不变而其余位变为0,我们可以用按位与运算符“&”,具体过程如下所示:

00011011 &

00000011 —————————

00000011

26

博学谷——让IT教学更简单,让IT学习更有效

最终结果是3。

现在我们修改条件,要求模16。此时我们依然可以看出,16的二进制数是10000,我们只需保证变量的前4位不变即可,具体过程如下所示:

00011011 &

00001111 —————————

00001011

最终结果是11。

由此我们可以得出结论,变量i模2的n次方的值可以用下面的方式表示: (i % 2 ^ n)与( i & (2 ^ n – 1))相等。

2.4.9 sizeof运算符

同一种数据类型在不同的编译系统中所占空间不一定相同,例如,在基于16位的编译系统中,int类型占用2个字节,而在32位的编译系统中,int类型占用4个字节。为了获取某一数据或数据类型在内存中所占的字节数,C语言提供了sizeof运算符,使用sizeof运算符获取数据字节数,其基本语法规则如下所示:

sizeof(数据类型名称); 或

sizeof(变量名称);

为了帮助大家更好地学习sizeof运算符,接下来通过一个案例来计算每种数据类型所占内存的大小,具体如例2-15所示。 例2-15

1 #include 2 void main() 3 { 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 }

// 通过类型名称计算各基本数据类型所占内存大小 printf(\ %d\\n\printf(\ %d\\n\printf(\ %d\\n\printf(\ %d\\n\printf(\

printf(\ %d\\n\printf(\printf(\ %d\\n\printf(\ %d\\n\// 通过变量名称计算变量所属数据类型占用内存大小 int val_int = 100;

double val_double = 100000.0;

printf(\ %d\\n\printf(\

运行结果如图2-21所示。

27

博学谷——让IT教学更简单,让IT学习更有效

图2-21 运行结果

从图2-21中可以看出,不同数据类型在内存中所占字节数都打印了出来,由此可见,使用sizeof关键字可以很方便地获取到数据或数据类型在内存中所占的字节数。除此之外,同学们还可以在Linux平台上用sizeof()运算符求出这些数据类型所占的大小。

2.4.10 运算符的优先级

在对一些比较复杂的表达式进行运算时,要明确表达式中所有运算符参与运算的先后顺序,这种顺序称作运算符的优先级。表2-15列出了C语言中运算符的优先级,数字越小优先级越高。

表2-15 运算符优先级

优先级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 运算符 . [] () ++ -- ~ ! (数据类型) * / % + - << >> >>> < > <= >= == != & ^ | && || ?:(三目运算符) = *= /= %= += -= <<= >>= >>>= &= ^= |= 根据表2-15所示的运算符优先级,分析下面代码的运行结果。

int a =2; int b = a + 3*a; printf(\

以上代码的运行结果为8,这是由于运算符“*”的优先级高于运算符“+”,因此先运算3*a,得到的结果是6,再将6与a相加,得到最后的结果8。

28

博学谷——让IT教学更简单,让IT学习更有效

int a =2; int b = (a+3) * a; printf(\

以上代码的运行结果为10,这是由于运算符“()”的优先级最高,因此先运算括号内的a+3,得到的结果是5,再将5与a相乘,得到最后的结果10。

其实没有必要去刻意记忆运算符的优先级。编写程序时,尽量使用括号“()”来实现想要的运算顺序,以免产生歧义。

?多学一招:运算符优先级口诀

虽然运算符优先级的规则较多,但也有口诀来帮助记忆,完整口诀是“单算移关与,异或逻条赋”,具体解释如下所示: ① “单”表示单目运算符:逻辑非(!)、按位取反(~)、自增(++)、自减(--)、取地址

(&)、取值(*);

② “算”表示算术运算符:乘、除、求余(*,/,%)级别高于加减(+,-); ③ “移”表示按位左移(<<)和位右移(>>);

④ “关”表示关系运算符:大小关系(>,>=,<,<=)级别高于相等不相等关系(==,!=); ⑤ “与”表示按位与(&); ⑥ “异”表示按位异或(^); ⑦ “或”表示按位或(|);

⑧ “逻”表示逻辑运算符,逻辑与(&&)级别高于逻辑或(||); ⑨ “条”表示条件运算符(? :);

⑩ “赋”表示赋值运算符(=,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=,!=); 需要注意的是,三目运算符(,)级别最低,口诀中没有表述,需另外记忆。

2.5 本章小结

本章主要讲解了C语言中的数据类型以及运算符。其中包括进制、基本数据类型、类型转换、运算符与表达式等。通过本章的学习,读者可以掌握C语言中数据类型及其运算的一些相关知识。熟练掌握本章的内容,可以为后面的学习打下坚实的基础。

在学习本章知识的过程中,初学者可能会遇到各种各样的问题,例如 “十进制如何转换成二进制”、“单精度和双精度浮点型的区别”、“前置 和后置自增自减运算符的区别”等。针对上述问题初学者可以扫描右侧 二维码关注问答精灵,让问答精灵帮你解决学习中的困难。

29

博学谷——让IT教学更简单,让IT学习更有效

2.6 习题

一、填空题

1、八进制必须以_____开头,十六进制必须以_______开头。 2、标识符只能由字母、数字和_______组成。

3、在计算机中的二进制表现形式有三种,分别是_____、_______、________。 4、C语言中的数据类型可分为4种,分别是基本类型、______、指针类型、_____。5、C语言提供了sizeof运算符,该运算符主要用于________。 二、判断题 1、执行语句 ++i; i=3; 后变量i的值为4。

2、隐式类型转换是指将取值范围大的数据类型转换为数据取值范围小的数据类型。 3、C 语言中的逻辑值“真”是用1表示的,逻辑值“假”是用0表示的。 4、C语言中赋值运算符比关系运算符的优先级高。

5、位运算符是专门针对数字0和1进行操作的。

三、选择题

1、下列选项中,可以正确定义C语言整型常量是?

A、32L B、51000 C、-1.00 D、567

2、算术运算符、赋值运算符和关系运算符的运算优先级按从高到低依次为? A、算术运算、赋值运算、关系运算 B、算术运算、关系运算、赋值运算 C、关系运算、赋值运算、算术运算 D、关系运算、算术运算、赋值运算 3、设整型变量 m,n,a,b,c,d 均为1,执行 (m=a>b)&&(n=c>d)后, m,n 的值是? A、0,0 B、0,1 C、1,0 D、1,1

4、若已定义 x 和 y为double 类型,则表达式 x=1;y=x+3/2 的值是?

30

博学谷——让IT教学更简单,让IT学习更有效

A、1 B、2 C、2.0 D、2.5

5、假设a=1,b=2,c=3,d=4则表达式a

1、请列举几个单目运算符、双目运算符、三目运算符并说明其区别? 2、请简述自增运算符放在变量前面和后面的区别? 五、编程题

1、已知梯形的上底为a,下底为b,高为h,请用程序实现求梯形的面积。2、请使用位运算符实现交换两个变量值的功能。

31

博学谷——让IT教学更简单,让IT学习更有效

A、1 B、2 C、2.0 D、2.5

5、假设a=1,b=2,c=3,d=4则表达式a

1、请列举几个单目运算符、双目运算符、三目运算符并说明其区别? 2、请简述自增运算符放在变量前面和后面的区别? 五、编程题

1、已知梯形的上底为a,下底为b,高为h,请用程序实现求梯形的面积。2、请使用位运算符实现交换两个变量值的功能。

31

本文来源:https://www.bwwdw.com/article/9cm5.html

Top