描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302483915丛书名: 21世纪普通高校计算机公共课程规划教材
目录
第1章C语言概述
1.1计算机系统
1.2计算机语言与程序设计语言
1.2.1计算机语言
1.2.2程序设计语言及其发展
1.3C语言的起源及特点
1.3.1热衷游戏与UNIX的起源
1.3.2UNIX的改进与C语言的起源
1.3.3C语言的特点
1.4C语言的标准化
1.4.1ANSI C/ISO C标准
1.4.2C99标准
1.4.3C11标准
1.5简单的C语言程序举例
1.5.1C语言程序的结构
1.5.2C语言的编程风格与规范
1.6C语言程序设计的一般步骤
1.7算法
1.7.1算法的概述
1.7.2算法的表示
小结
习题
第2章顺序结构程序设计
2.1简单的顺序结构程序
2.1.1标识符、关键字、常量、变量
2.1.2运算符、表达式、语句
2.1.3格式化输入、输出
2.2数据类型
2.2.1整型类型
2.2.2浮点类型
2.2.3字符类型
2.3输入输出
2.3.1字符型数据的输入和输出
2.3.2数值型数据的输入和输出
2.3.3数值与字符混合输入和输出
小结
习题
第3章运算符与表达式
3.1运算符和表达式中的基本概念
3.2算术运算符及算术表达式
3.3逻辑、关系运算符及其表达式
3.4赋值运算符及赋值表达式
3.5移位运算符及移位表达式
3.6sizeof运算符及其表达式
3.7逗号运算符及逗号表达式
3.8运算符的优先级与结合性
3.9类型转换
3.9.1自动类型转换
3.9.2强制类型转换
小结
习题
第4章分支结构
4.1if语句
4.2ifelse语句与条件表达式
4.2.1ifelse语句
4.2.2条件表达式
4.3if语句嵌套
4.4级联elseif多分支语句
4.5switchcase多分支结构
小结
习题
第5章循环结构
5.1while循环
5.2dowhile循环
5.3while和dowhile的关系
5.3.1while和dowhile的等价关系
5.3.2while和dowhile的不等关系
5.4for循环
5.5循环的嵌套结构
5.6执行流程跳转语句
5.6.1goto语句
5.6.2break语句
5.6.3continue语句
5.7综合举例
小结
习题
第6章数组
6.1一维数组
6.1.1一维数组的定义
6.1.2一维数组的引用
6.1.3一维数组的初始化
6.2查找和排序算法
6.2.1顺序查找
6.2.2气泡排序
6.2.3选择排序
6.3二维数组
6.3.1二维数组的定义
6.3.2二维数组的引用
6.3.3二维数组的初始化
6.3.4二维数组的存储
6.3.5二维数组的应用举例
6.4一维字符数组
6.4.1一维字符数组的定义及初始化
6.4.2一维字符数组的引用
6.4.3一维字符数组的应用举例
6.5字符串处理函数
6.6二维字符数组
6.6.1二维字符数组的定义及初始化
6.6.2二维字符数组的引用
6.6.3二维字符数组的应用举例
6.7数组综合举例
小结
习题
第7章函数
7.1函数的定义
7.1.1函数定义格式
7.1.2函数返回值
7.2函数的调用
7.2.1函数调用格式
7.2.2函数调用过程
7.2.3函数原型声明
7.2.4函数调用举例
7.3函数的嵌套调用
7.4传值调用和传址调用
7.5递归函数
7.6变量的作用域和生存期
7.6.1变量的作用域
7.6.2存储区和存储类型
小结
习题
第8章指针
8.1指针的定义与引用
8.1.1内存与地址
8.1.2指针变量的定义
8.1.3指针变量的引用
8.2指针与数组
8.2.1一维数组和指针
8.2.2二维数组和指针
8.2.3数组指针和指针数组
8.3指针与字符串
8.3.1常量字符串与指针
8.3.2变量字符串
8.4指针与函数
8.4.1指针作函数形参——传址调用
8.4.2指针作函数返回类型——指针函数
8.4.3指向函数的指针——函数指针
8.5二级指针
8.5.1二级指针的定义
8.5.2二级指针与二维数组
8.5.3二级指针与指针数组
8.6动态内存分配与指针
8.6.1无类型指针和空指针
8.6.2常见动态内存申请和释放函数
小结
习题
第9章自定义类型
9.1结构体类型及其变量
9.1.1结构体类型的引入
9.1.2结构体类型定义
9.1.3结构体类型的变量
9.1.4结构体变量成员的引用
9.1.5结构体变量的存储
9.2结构体数组
9.2.1结构体数组定义与使用
9.2.2结构体数组的应用
9.2.3类型同义词
9.3结构体指针与结构体数组
9.4结构体与函数
9.5单链表
9.5.1数据的存储结构
9.5.2单链表
9.5.3单向循环链表
9.6共用体
9.6.1共用体类型及其变量的定义
9.6.2字节存储机制
9.7枚举类型
小结
习题
第10章输入和输出
10.1文件及其分类
10.2文本文件与二进制文件
10.2.1文本文件与二进制文件
10.2.2C语言与文件读写
10.2.3缓冲和非缓冲文件系统
10.3文件的打开与关闭
10.4文件的顺序读写
10.4.1按字符输入输出
10.4.2按字符串输入输出
10.4.3按格式化输入输出
10.4.4按二进制方式读写数据块
10.5文件的随机读写
小结
习题
第11章预处理和位操作
11.1预处理指令与预处理器
11.2宏定义
11.2.1无参宏定义
11.2.2带参宏定义
11.2.3带参宏调用与函数调用
11.3条件编译
11.4位操作
11.4.1原码、反码、补码
11.4.2位操作符
小结
习题
参考文献
附录
附录AVC 6.0环境中开发C程序的步骤
附录BASCII表
附录C运算符的优先级和结合性
附录DANSI C常用库函数
前言
未来的世界是自动化一统天下的时代,智能控制离不开程序,由于C语言是所有高级程序设计语言中最接近硬件且可移植性较好的高效编程语言,其在系统编程和嵌入式开发中具有不可比拟的优势。而且学习C语言是进一步学习其他高级编程语言,如C 和Java的基础。
【本书主要内容】本书共分为11章,内容包括C语言概述、顺序结构程序设计、运算符与表达式、分支结构、循环结构、数组、函数、指针、自定义类型、输入和输出、预处理和位操作等。本教材在讲解C语言基础知识时,力求简单、明了,并均有对应实例。而对于C语言的精髓部分,如函数、指针、自定义类型等做了由浅入深的详细讲解,并设计了大量的例题和实例。从文件中获取数据进行处理,然后把处理的结果保存到文件中,这可能是多数读者通过学习C语言渴望掌握的技能。故本教材把“输入和输出”作为非常重要的章节,该章节涉及对整本教材的复习巩固提高。该章配以大量的常用实例,提高读者的学习兴趣。第11章“预处理和位操作”通常是被C语言读者忽略的部分,而该部分在实际的程序开发中占有非常重要的地位,如涉及带参、无参宏定义,以及如何使用预处理命令避免头文件的重复包含等问题。如果能使用位运算代替程序中的乘除等算术运算,将提高运算效率,且能体现掌握C语言的深度。
【本书特色】(1) 把枯燥、复杂的语法概念简单化、实例化。本教材几乎对涉及的所有知识点均设计实例进行讲解,通俗易懂,便于读者自学。(2) 例题设计具有代表性,把在实际程序开发过程中经常遇到的错误及不规范的语法,均以例题的形式进行分析总结,分析思路详尽,所有实例均提供了源代码,便于读者使用。(3) 每一节讲解完均有对应的复习思考题,便于对本节重点知识及时巩固提高。(4) 分章节分知识点设计的课后习题结构,几乎覆盖了所有重要知识点,且均是在例题及复习思考题基础上的提升,能够让读者由浅入深地加深对知识点的理解,便于及时复习巩固知识点的学习。(5) 本教材所有例题、习题均严格遵守业界较通用的编程规范,设计结构合理,思路清晰,注重程序的可读性和健壮性。(6) 每章小结均以表格的形式列出本章的重点、难点及常见易错点,结构清晰,便于读者复习把握。
【C语言学习的误区】(1) 只注重功能实现,不注重编程规范,导致代码的可读性及可维护性较差。(2) 认为只要编写的代码在编译阶段没有错误,且能运行出正确结果就万事大吉了。编译器不是万能的,尤其是C编译器语法检查不够严格,有些潜在的风险难以发现。且由于测试用例的有限性和片面性,一两次的正确运行结果不能保证程序的绝对正确性。(3) 不注重代码调试。当发现编译错误时,根据编译器的提示很容易发现并修改,出现结果错误时,采用从前往后逐条分析代码的方法排查错误,认为掌握这种方法和具备这种能力已足够了,而不采用科学的代码调试工具进行排查错误。(4) 仅为了等级考试,死记硬背枯燥的知识点、套题型,造成学习枯燥,所掌握的全是零散的、片面的知识点的堆砌,只见树木不见森林。
【C语言学习的建议】(1) 在学习C语言的过程中始终要围绕着锻炼编程思维和解决问题的能力,注重编程素养的提高而进行。(2) 注重读写。多读程序,不仅只读规范的好程序,从而借鉴优秀程序的优点,还要以批判的精神对不规范,可读性、健壮性差的程序提出修改意见。多写程序,在写程序之前一定要有清晰的算法设计思路,选择好的数据结构和程序结构。运行正确后,要思考改进,久而久之,使用C语言解决问题的能力会得到进一步提高。(3) 注重调试能力。程序设计中出现错误在所难免,这里所指的错误不仅是C语法错误,还包括逻辑错误。而后者是比较难发现和解决的,要使用调试工具,通过设置断点,进行代码走读,逐步缩小错误源的范围,最终找到并解决。本书全部章节均由孙海洋编写,在编写过程中得到了黄润生院长、赵志宏院长及其他教师的大力帮助和支持,在此一并向他们表示衷心的感谢!由于编者水平有限、时间仓促,书中疏漏和不足之处在所难免,恳请专家、读者批评指正。
编著者2018年于南京大学金陵学院
本章学习目标 掌握常见的运算符的优先级与结合性 掌握各种常见的表达式 根据优先级和结合性分析复杂表达式的执行顺序
本章首先向读者介绍运算符的基本概念,如左值、右值、运算符操作数个数等。接着介绍常见的运算符及其对应的表达式。然后介绍运算符的优先级和结合性,并重点介绍根据优先级和结合性分析复杂表达式的执行顺序。最后介绍强制类型转换运算符及其使用。3.1运算符和表达式中的基本概念程序无非是对各种关系(数值关系、逻辑关系等)进行操作的代码集合,对关系的操作都可以看成是对数据的操作,对不同数据的操作,C语言提供了对应的运算符。使用运算符把操作数结合起来形成的式子,称为表达式。在讲解具体运算符之前,介绍几个与之相关的术语: 操作数(operand)、运算符(operator)、左值(lvalue)和右值(rvalue)。操作数(operand)是程序操纵的数据实体,该数据可以是数值、逻辑值或其他类型。该操作数既可以是常量也可以为变量。例如:
int a=3;
int b=a 2;
加运算符’ ‘,取出变量a中的值3,与常量2相加,并把求和表达式a 2的结果5保存到变量b中。运算符(operator)是可以对数据进行相应操作的符号。如对数据求和操作,用加法运算符’ ‘,求积操作使用乘法运算符’*’等。根据运算符可操作的操作数的个数,可把运算符分为一元运算符、二元运算符和多元运算符(一般三元)。C语言提供了丰富的运算符,有: 算术运算符、关系运算符、逻辑运算符、赋值运算符、移位运算符、逗号运算符及sizeof运算符。对应有: 算术表达式、关系表达式、逻辑表达式、赋值表达式、移位表达式、逗号表达式及sizeof表达式等,本节将介绍常见的运算符及对应表达式。3.2算术运算符及算术表达式算术运算符按操作数个数可分为一元运算符(含一个操作数)和二元运算符(含两个操作数)。一元运算符的优先级一般高于二元运算符。一元运算符: (正号)、-(负号)、 (增1)、–(减1)。二元运算符: (求和)、-(求差)、*(求积)、/(求商)、%(求余)。1. 符号运算符: (正号)、-(负号)’ ‘(正号)表示不改变操作数的值及符号,如23也可表示为 23,编译器不报错。而’-‘(负号)可用于得到一个数的相反数。例如:
int a=-5;
int b=-a;
在变量a前加-(负号)后赋值给b,即把a的相反数赋给b。
2. 自增量运算符: (增1)、–(减1)自增量运算符均有两种使用形式, a、a 及–a、a–,也称为前缀形式和后缀形式。在讲解自增量运算符的两种形式之前,先介绍下左值(lvalue)和右值(rvalue)的概念。计算机内存中可修改的存储对象,一般称为左值或lvalue。例如:
int a;//整型变量a可以作为左值使用
float b; //单精度浮点型变量b也可作为左值使用
const int c; //因为常变量c的值不允许改变,故不可作为左值使用
把可赋值给左值的量称为右值或rvalue。右值可以是常量、变量或者表达式。例如:
int a,b; //定义整型变量a和b
a=2; //把常量2作为右值,赋给左值a
b=a; //把变量a作为右值,赋给左值b
b=a 3; //把表达式a 3的值作为右值,赋给左值b
前缀形式: 如 a为前缀加形式的增1表达式,表示把变量a的值加1后的值作为该表达式的值,同时变量a本身的值加1; –a类似,表示把变量a的值减1后的值作为该表达式的值,同时变量a本身的值减1。例如:
int a=5,b;
则语句b=a ;与b= a;的含义不同。若采用第一条赋值语句,则直接把a的原值5赋给变量b。若采用第二条赋值语句,则把a的原值5加1后的值6赋给变量b。但相同的是,这两种赋值方式均使变量a自身的值增了1,即执行完后,a均为6。后缀形式: 如a 为后缀加形式的增1表达式,表示先直接把变量a原来的值作为该表达式的值,然后变量a本身的值加1; a–类似,表示先直接把变量a原来的值作为该表达式的值,然后变量a本身的值减1。注意: 浮点型变量也同样支持自增量运算操作。例如:
float a=3.2f;
a ;
printf(“a=%f\n”,a);
执行完自增量运算后,输出a=4.200000。建议在实际编程中,应尽量避免对浮点型变量进行自增量运算操作。通过下面的例子,掌握前缀增1与后缀增1两种使用形式的异同。例1分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a=2,b,c,d;
b= a 4;
c=3*a ;
d=a–*3;
printf(“a=%d,b=%d,c=%d,d=%d”,a,b,c,d);
return 0;
}
【分析】(1) b= a 4;该语句中运用到三个运算符: 前缀增1运算符 、加法运算符 和赋值运算符=,三个运算符的优先级是一元运算符 最高,其次是求和,最低的是赋值运算符。该语句等价于b=( a) 4;先取变量a的值2加1后的结果3作为 a表达式的值,然后把该表达式的值3与4求和的值7赋值给变量b,即b值为7。执行完该语句后变量a自身值增1,其值变为3。(2) c=3*a ;等价于c=3*(a );表示把3与a 求积的结果赋给c,而a 表达式表示先把变量a的值3作为该表达式的值,即c=3*3;同时变量a自身值增1,变为4。(3) d=a–*3;先取变量a的值4作为表达式a–的值,把4*3的值12赋给变量d。同时变量a自身减1,变为3。(4) printf(“a=%d,b=%d,c=%d,d=%d”,a,b,c,d);双引号中有4个输出格式控制符,依次使用输出列表中4个输出项a、b、c、d的值替换。【运行结果】
a=3,b=7,c=9,d=12
3. 增1、减1运算符的副作用注意: 增1、减1运算符是具有副作用的运算符,即不仅能改变表达式的值,也改变了变量自身的值。使用时要慎重,尤其以下两种情况,要避免使用。(1) 当一个变量多次出现在某表达式中时,建议不要将增1或减1运算符应用于该变量。例如:
int a=1; //定义整型变量a,并赋初值1。
int b; //定义整型变量b,未初始化。
b=a a ; //杜绝编写类似的表达式!
a a 该表达式的值到底是两次取a的原值1相加的结果2赋给b,即b=2=1 1,还是按从左到右取a原值1作为第一个a 表达式的值,同时变量a增1变为2。第二个表达式的值为取a的值2作为表达式的值,同时变量a增1,变为3,这样b=3=1 2,还是从右到左依次运算。C标准没有对此进行统一规定,不同的编译器可能得到不同的结果。死记硬背这类操作的规则毫无意义!为了增强代码的可读性及可移植性,并避免产生歧义性,这种非标准的语法一定要慎用或不用。(2) 多参函数调用时,如果一个变量出现在多个实参中时,不要对该变量使用增1或减1运算符。原因同上,即不同编译器可能得到不同结果,出现歧义性。4. 相除/、%(求余)1) 相除运算符/(1) 当运算符/的操作数(被除数和除数)均为整数时,结果为取商(取整)。例如: 16/5结果为两数相除的商3。(2) 当运算符/的操作数中有一个或两个浮点数时,结果与数学中除法运算相同,包含整数部分和小数部分。例如: 8/2.5结果为3.2。2) 取余运算符%(1) 当运算符%的操作数(被除数和除数)均为整数时,结果为取余。例如: 16%5结果为两数相除的余数1。(2) 当运算符%的操作数中有一个或两个浮点数时,语法错误。例如: 8%2.5语法错误。运算符%两操作数都必须为整数,否则语法错误。在程序设计中,经常使用求商和求余运算符分解整数的各位数字。例如,分解十进制整数123的个位、十位和百位数字,可以有多种不同的分解方案,下面是其中一种方案。
int a=123,g,s,b; //g:个位 s:十位 b:百位
g=a%10; //g=3
s=a/10%10; //s=2
b=a/100; //b=1
【复习思考题】1. 简述前缀增1与后缀增1运算符的异同点。2. 自增量运算操作仅适用于整型吗?举例说明。3. 求商运算符/与求余运算符%有何区别?举例说明。4. 如何分离一个十进制数的各位数字?至少写出两种不同的方法。3.3逻辑、关系运算符及其表达式1. 逻辑运算符
C语言提供了以下三种逻辑运算符。(1) 一元: !(逻辑非)。(2) 二元: &&(逻辑与)、‖(逻辑或)。以上三种逻辑运算符中,逻辑非!的优先级最高,逻辑与&&次之,逻辑或‖优先级最低。算术、逻辑、赋值运算符的优先级顺序为:
逻辑非! >算术>逻辑与&&、逻辑或‖ >赋值=
逻辑表达式的值为逻辑值,即布尔型(bool),该类型为C99新增的,一些编译器可能还不支持该类型。逻辑值分为逻辑真值和逻辑假值。一般情况下,在判断时,仅有零值被判断为逻辑假值(false),一切非零值均可被判断为逻辑真值(true); 在存储和表示时,通常,使用1表示和存储逻辑真值,使用0表示和存储逻辑假值。逻辑与&&运算符的运算规则: 只有两个操作数均为逻辑真时,结果才为真。其余情况,结果均为假。逻辑或‖运算符的运算规则: 只有两个操作数均为逻辑假时,结果才为假。其余情况,结果均为真。例如,设有定义语句int a=3,b=5; 则:
!a 由于a非零,为真,!a为假,其值为0
a‖b 由于a和b均非零,均为真,故逻辑或的结果为真,其值为1
a&&b 由于a和b均非零,均为真,故逻辑与的结果为真,其值为1
!a‖b&&2 由于逻辑非!优先级最高,首先与a结合,而&&优先级高于‖,相当于(!a)‖(b&&2)即0‖1为真,其值为1。
逻辑与&&、逻辑或‖均有“短路”特性。(1) 逻辑与&&“短路”: 当逻辑与&&的左操作数为逻辑假时,就足以判断该逻辑运算的结果为假了,故右操作数就不再被执行。(2) 逻辑或‖“短路”: 当逻辑或‖的左操作数为逻辑真时,就足以判断该逻辑运算的结果为真了,故右操作数就不再被执行。例如:
int a=1,b=2,c;
c=a‖ b;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
由于a为非零值,即为真,而当逻辑或‖的左操作数为真时,就足以判断该逻辑操作的结果为真。故发生“短路”,即右操作数 b不被执行。输出结果为: a=1,b=2,c=1。例2分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a=0,b=2,c;
c=!a‖ b&&a–;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
return 0;
}
【分析】(1) 混合表达式c=!a‖ b&&a–中含有的运算符有逻辑非!、逻辑或‖、逻辑与&&、算术前缀 、算术后缀–、赋值号=等6个运算符。逻辑运算符、算术运算符、赋值运算符的优先级的关系为:
逻辑非! >算术>逻辑与&&、逻辑或‖ >赋值=
由于该表达式中赋值运算符优先级最低,故最后赋值。(2) 根据优先级的高低,表达式!a‖ b&&a–等价于: (!a)‖(( b)&&(a–)),而逻辑或‖的左操作数!a为真,此时足以判断该表达式的值为真。故发生“短路”,即‖的整个右操作数(( b)&&(a–))不再被执行。【运行结果】
a=0,b=2,c=1
2. 关系运算符C语言提供的关系运算符有: >(大于)、>=(大于等于)、b即为关系表达式,在C语言中,同逻辑表达式一样,关系表达式的值也为逻辑值,即布尔型(bool),取值为真或假。算术、逻辑、关系、赋值运算符的优先级顺序为:
逻辑非! >算术>关系>逻辑与&&、逻辑或‖ >赋值=
例如: int a=3,b=5; 则:
a>b逻辑假,其值为0
a>=b 逻辑假,其值为0
aa<=b 逻辑真,其值为1
a==b 逻辑假,其值为0
a!=b 逻辑真,其值为1
例3分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a=0,b=1,c;
c=a>=b‖b >1;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
return 0;
}
【分析】根据运算符的优先级,表达式a>=b‖b >1等价于(a>=b)‖(b >1)。a>=b为假,其值为0,逻辑或‖不会发生“短路”,接着计算逻辑或‖的右操作数b >1,由于是后缀加1,故先取b的原值1与1比较大小,由于1>1为假,故逻辑或‖的右操作数也为假,假‖假=假,故c的值为0。执行了一次b 运算,故b的自身值增了1,变为2。【运行结果】
a=0,b=2,c=0
【复习思考题】1. 列举所有的逻辑运算符与关系运算符。2. 按从高到低排列: 逻辑运算符、算术运算符、关系运算符及赋值运算符的优先级顺序。3. 逻辑表达式的运算规则是什么?4. 逻辑运算中“短路”的含义是什么?设计一个具有“短路”特性的表达式,进行验证。
3.4赋值运算符及赋值表达式赋值操作是程序设计中最常用的操作之一,C语言共提供了11个赋值运算符,均为二元运算符,其中仅有一个为基本赋值运算符=,其余10个均是复合赋值运算符。基本赋值运算符: =。复合赋值运算符: =(加赋值)、-=(减赋值)、*=(乘赋值)、/=(除赋值)、%=(求余赋值)、<<=(左移赋值)、>>=(右移赋值)、&=(按位与赋值)、|=(按位或赋值)和^=(按位异或赋值)等。赋值操作的优先级较低,仅高于逗号运算符。1. 基本赋值=如int a=5;表示把5赋值给整型变量a,不能读成a等于5。赋值号左边必须为一左值,赋值号右边的右值可以为常量、变量或表达式。如下赋值均是正确的。
int a,b;//定义整型变量a和b
a=3; //把常量3赋值给a,右值为常量
b=a; //把变量a的值赋给b,右值为变量
b=a 3; //把求和表达式a 3的值赋给b,右值为表达式
以下赋值均是错误的。
int a=2;
3=a; //错误,常量3不能作为左值
const int b=5; //定义整型常变量或只读变量b,并初始化为5,其值不能被改变
b=1; //错误,企图改变常变量的值,即常变量不能作左值
2. 复合赋值: =、-=、*=、/=、%=a =b; 等价于a=a b;a-=b; 等价于a=a-b;a*=b; 等价于a=a*b;a/=b; 等价于a=a/b;例如:
int a=5;
a =3; //等价于a=a 3;
由于赋值运算符的优先级很低,仅高于逗号运算符,故最后做赋值操作。a =3 2;等价于a=a (3 2);通过下面的例子,掌握上述4种复合赋值运算符,并熟练掌握printf的使用。例4分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a=1,b=2,c=3; //定义三个整型变量,并初始化
float d=10.2f; //定义float变量d,用浮点常量10.2初始化
a =1; //相当于a=a 1;即a=1 1;a等于2
b-=a 5;
c*=a-4;
printf(“%d,%d,%d,%f”,a,b,c,d/=a);
return 0;
}
【分析】(1) float d=10.2f;如果改为float d=10.2;虽然没有语法错误,可以正常运行,但一般编译器会提示warning(警告),原因是编译器会把10.2等常量默认当成double型常量处理,与d的类型float不一致,故出现警告。因此可通过加f明确10.2为float型常量。(2) a =1;相当于a=a 1;求出a为2。(3) b-=a 5;由于赋值运算符的优先级低于算术求和运算符,故该语句等价于b=b-(a 5);,即b=2-(2 5);,得b=-5; 同理c*=a-4;c=3*(2-4);,故c=-6。(4) printf(“%d,%d,%d,%f”,a,b,c,d/=a);由于输出列表中a、b和c均为int型变量,故输出格式占位符均为%d; 输出列表中第4项为表达式,其表达式的值为d=d /a=10.2f/2=5.1,为浮点类型,输出格式占位符为%f,在VC 6.0环境中,float类型为小数点后保留6位数字。【运行结果】
2,-5,-6,5.100000
【复习思考题】当表达式中含有多个复合赋值运算符时,表达式的执行规则是什么?
3.5移位运算符及移位表达式使用左移运算符<>,只能改变该左移表达式或右移表达式的值,并不会改变变量本身的值; 如要想在左移或右移过程中,改变变量本身的值,可使用左移赋值运算符<<=或右移赋值运算符>>=。本节仅简单介绍移位操作的基本知识,有关移位操作的更多知识将在“位操作”章节讲解。1. 左移运算符<>注意: 本章节所涉及的移位操作仅针对正数移位的情况,有关负数移位的相关知识将在“位操作”章节中讲解。将运算数的各二进制位均右移若干位,左端补0,右边移出的位丢弃。则每右移一位,相当于该数除以2取整。例如: 计算10右移一位的结果。若使用8位二进制数表示10,即0000 1010,右移一位后,最右端的一位丢弃,左端补一个0,为: 0000 0101[0],即右移一位后的8位二进制数为: 0000 0101,对应十进制数为: 1×22 1×20=5,即右移一位,相当于该数除以2取整。10右移两位,相当于把0000 1010右端两位丢弃,左端高位补两个0,即: 0000 0010[10]。为2,相当于10/22=10/4=2。例5分析下列程序的输出结果。思考: 把程序中前两个printf语句合并为printf(“%d,%d\n”,a << 2,a);,后两条合并为printf(“%d,%d\n”,b <<= 2,b);,分析输出结果。【程序代码】
#include
int main(void)
{
char a=5,b=3;
printf(“%d\n”,a<<2);//以十进制整数形式输出表达式a<<2的值
printf(“%d\n”,a); //输出a的值
printf(“%d\n”,b<<=2); //输出左移赋值表达式b=<<2的值
printf(“%d\n”,b);
return 0;
}
【运行结果】
20
5
12
12
【分析】(1) 定义两个字符型变量a和b,每个占8位,并初始化为5和3,其内存中为a: 0000 0101,b: 0000 0011,故表达式a<<1的值为0000 1010即10相当于原值5的2倍,10=5×21,表达式a<<2的值为0001 0100即20相当于原值5的4倍,20=5×22=5×4=20。(2) printf(“%d\n”,a<<2);输出表达式a<<2的值为5×22=20,但a的值并未发生改变,依然是5。(3) printf(“%d\n”,b<<=2);左移赋值表达式b<<=2等价于b=b<<2;,该表达式的值为3×22=12,该表达式的值赋值给了b,故变量b的值与表达式的值相同,也为12。【说明】(1) 合并为两条printf语句后的输出为什么与单条输出结果不一致?原因在于: printf从最右端的一个输出参数列表开始计算,依次向左。如printf(“%d,%d\n”,b<<=2,b);先计算最右边第一个的参数列表即b为3,右边数第二个输出参数列表为左移赋值表达式b<<=2,该表达式的值为12。(2) 如果把四条printf语句合并为两条输出语句后,程序的输出结果为:
20,5
12,3
对&=、|=和^=这三种复合赋值运算符本章暂不做介绍。
3.6sizeof运算符及其表达式运算符sizeof可用于求出某种类型或变量所占的字节数,返回字节数对应的整数值,该值与具体的机器位数及C编译器有关。例如,在32位机器上,使用sizoef求整型int所占字节数,在Turbo C中返回2,而在VC 6.0中则返回4。如下使用方法均是正确的。
int a=2;
sizeof(a); //求某变量所占字节数
sizeof a; //当求变量字节数时,可省略括号,但不推荐该用法
sizeof(int); //求某种类型所占字节数
如下使用是错误的。
sizeof int; //错误,测试某种类型的字节数时,必须加括号
为了避免出现错误,统一编程风格起见,使用sizeof运算符不管是测试某变量还是某类型所占的字节数时,均加括号。3.7逗号运算符及逗号表达式逗号运算符在C语言常见的运算符中,优先级最低。使用逗号运算符,把多个表达式连接起来组成逗号表达式,逗号表达式中最后一个表达式的值作为整个逗号表达式的值。由于逗号运算符的优先级最低,为了避免不必要的错误,建议用括号把整个逗号表达式括起来作为一个整体参与运算。例如:
int a=3,b=5,t;
t=a,a=b,b=t;
上述是使用逗号把三个赋值表达式连接起来形成的逗号表达式,从左向右依次执行各个表达式,上述表达式语句等价于如下三条赋值语句。
t=a;
a=b;
b=t;
该程序段的功能是交换变量a和b的值。执行完该程序段后,a=5,b=3。通过如下例题,掌握逗号表达式的使用方法及注意事项。例6分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a1,a2,a3,b,c,d;
a1=(b=6,c=7,d=5); //赋值表达式d=5作为逗号表达式的值,赋给a1
a2=( b,c–,d 1); //求和表达式d 1作为逗号表达式的值,赋给a2
a3= b,c–,d 1;
printf(“%d,%d,%d\n”,a1,a2,a3);
return 0;
}
【分析】(1) a1=(b=6,c=7,d=5);括号内为把三个赋值表达式用逗号运算符连接起来的逗号表达式。最右端一个表达式的d=5作为整个逗号表达式的值,并把该值赋值给a1,可等价为a1=d=5;故a1等于5; 同理执行完该语句a2=( b,c–,d 1);后,b得7,c得6,a2=d 1,故a2=6。(2) 与前两个语句不同,a3= b,c–,d 1;由于赋值运算符优先级高于逗号运算符优先级,故这条语句仅为逗号表达式语句,a3= b仅作为该逗号表达式中的第一条语句,把 b表达式的值7 1=8,赋给a3; 而整个逗号表达式的值d 1=6并未参与其他运算。综上分析可得,a1=5,a2=6,a3=8。【运行结果】
5,6,8
【说明】(1) 如果输出替换成
printf(“%d,%d,%d”,a1,a2,(a3= b,c–,d 1));
该输出列表中从右端数第一项为逗号表达式(a3= b,c–,d 1),该逗号表达式的值为d 1=5 1=6。该逗号表达式对a1、a2的值没有影响,故输出为5,6,6。(2) 如果把该逗号表达式的括号去掉变成:
printf(“%d,%d,%d”,a1,a2,a3= b,c–,d 1);
编译器将认为输出列表有5项,与输出格式占位符个数三个不匹配,将得到错误结果。故使用逗号表达式时建议勿忘加括号。
3.8运算符的优先级与结合性所谓运算符的优先级是指在复合表达式中不同运算符的执行顺序。C语言中运算符的优先级分为15级,1级为最高,15级最低。而运算符的结合性,则是指当相邻的运算符具有相同的优先级时,表达式的结合方向。C语言中运算符的结合性分为两种: 左结合性(从左向右)、右结合性(从右向左)。左结合性: 把优先级相同且相邻的各个运算符,按从左到右的顺序依次为其组成清晰完整的表达式。右结合性: 把优先级相同且相邻的各个运算符,按从右到左的顺序依次为其组成清晰完整的表达式。关于C语言中运算符的优先级和结合性,请查阅本教材附录C。其实在前几节运算符的讲解中,已经涉及运算符的优先级和结合性,本节仅对常用的几类运算符的优先级和结合性进行讲解。(1) 算术运算符( 、-、*/)的结合性均是左结合性,即从左向右。例如:
1 2 3 4
该表达式中有三个相邻且相同的 运算符,优先级相同,故要考虑该运算符的结合性。由于算术运算符 的结合性是左结合性,即从左到右,分析过程如下。第1步: 先把最左端的 运算符组成一个完整的加法表达式,而最左端的 运算符已经有确定的左操作数1,只需要再结合一个右操作数2即可为该运算符构成完整的加法表达式。等价于:
(1 2) 3 4
第2步: 把从左端起第二个 运算符组成完整的加法表达式,该运算符的左操作数为(1 2)已确定,只需再为其结合一个右操作数3即可为该运算符构成完整的加法表达式。等价于:
((1 2) 3) 4
第3步: 把最右端的 运算符组成完整的加法表达式,该运算符的左操作数((1 2) 3)已经确定,而此时该运算符的右操作数只能为4,无歧义。无须再加括号显式指定。(2) 赋值运算符的结合性均是右结合性,即从右向左。例如:
int a,b,c;
a=b=c=6;
上述语句中,赋值表达式a=b=c=6中含有三个相邻的赋值号=,优先级相同,故要考虑该运算符的结合性。由于该运算符的结合性是右结合性,即从右向左,分析过程如下。第1步: 先把最右端的=运算符组成一个完整的赋值表达式,而最右端的=运算符已经有确定的右操作数,即右值6,只需要再结合一个左操作数,即左值c,即可为该运算符构成完整的赋值表达式。等价于:
a=b=(c=6)
第2步: 把从右端起第二个=运算符组成完整的赋值表达式,该运算符的右操作数,即右值为(c=6)已确定,只需再为其结合一个左操作数,即左值b,即可为该运算符构成完整的赋值表达式。等价于:
a=(b=(c=6))
第3步: 把最左端的=运算符组成完整的赋值表达式,该运算符的右操作数,即右值(b=(c=6))已经确定,而此时该运算符的左操作数,即左值只能为a,无歧义。无须再加括号显式指定。再比如:
int a=5,b=9;
a =b*=a/=2;
执行完上述表达式后,a的值是多少?由于复合赋值运算符 =、-=、*=、/=、%=等运算符的优先级属于一个等级,即均相同,故要考虑该类运算符的结合性。由于赋值运算符的结合性是右结合性,即从右向左,分析过程如下。第1步: 先把最右端的/=运算符组成一个完整的复合赋值表达式,而最右端的/=运算符已经有确定的右操作数,即右值2,只需要再结合一个左操作数,即左值a,即可为该运算符构成完整的复合赋值表达式。等价于: a =b*=(a/=2)。此时a的值为: a/=2等价于a=a/2=5/2=2。第2步: 把从右端起第二个*=运算符组成完整的复合赋值表达式,该运算符的右操作数,即右值为(a/=2)即a的值2已确定,只需再为其结合一个左操作数,即左值b,即可为该运算符构成完整的复合赋值表达式。等价于: a =(b*=(a/=2))。相当于a =(b*=2)。b*=2等价于b=b*2=9*2=18。第3步: 把最左端的 =运算符组成完整的复合赋值表达式,该运算符的右操作数,即右值(b*=(a/=2))即b的值18已经确定,而此时该运算符的左操作数,即左值只能为a,无歧义。无须再加括号显式指定。等价于: a =(b*=(a/=2))。相当于a =18,而此时a为2。故等价于a=a 18=2 18=20。综上所述,表达式a =b*=a/=2的结合顺序为a =(b*=(a/=2))。【复习思考题】1. 运算符的优先级和结合性的含义是什么?2. 结合性的分类及各自的运算规则是什么?3. 设有int a=3;执行a =a-=a*=a/=a;后a的值是多少?4. 设有int a=5;则语句a =3 a*=a 2;正确吗?如果错误,请分析其错误原因。3.9类 型 转 换计算机硬件进行算术操作时,要求各操作数的类型具有相同的大小(存储位数)及存储方式。例如,由于各操作数大小不同,硬件不能将char型(1字节)数据与int型(2或4字节)数据直接参与运算; 由于存储方式的不同,也不能将int型数据与float型数据直接参与运算。然而,由于C语言编程的灵活性,在一个表达式或一条语句中,允许不同类型的数据混合运算。C语言的灵活性与计算机硬件的机械性是一对矛盾,如处理不好,将会产生错误结果。对于某些类型的转换编译器可隐式地自动进行,不需人工干预,称这种转换为自动类型转换; 而有些类型转换需要编程者显式指定,通常,把这种类型转换称为强制类型转换。3.9.1自动类型转换一个表达式中出现不同类型间的混合运算,较低类型将自动向较高类型转换。不同数据类型之间的差别在于数据的表示范围及精度上,一般情况下,数据的表示范围越大、精度越高,其类型也越“高级”。整型类型级别从低到高依次为:
int→unsigned int→long→unsigned long→long long→unsigned long long
浮点型级别从低到高依次为:
float→double
1. 操作数中没有浮点型数据时当char、unsigned char、short或unsigned short出现在表达式中参与运算时,一般将其自动转换为int类型,特殊情况下unsigned short也可能转换成unsigned int(如Turbo C2.0中,short和int所占字节数相同,unsigned short的正数表示范围比int大,故应转换为unsigned int)。int与unsigned int混合运算时:
int→unsigned int
int、unsigned int与long混合运算时,均转换为long类型。2. 操作数中有浮点型数据时当操作数中含有浮点型数据(float或double)时,所有操作数都将转换为double型。例如:
3 5.3f 1.7
上述算术表达式中操作数1.7为双精度浮点数,故先把3和单精度浮点数5.3自动提升为双精度浮点数后,参与运算。运算结果为双精度浮点数10.0。3. 赋值运算符两侧的类型不一致时当赋值运算符的右值(可能为常量、变量或表达式)类型与左值类型不一致时,将右值类型可能提升或降低为左值类型。例如:
double d;
d=5.1f;
由于左值为双精度浮点型,故先把右值单精度浮点型常量5.1提升为双精度浮点型后,再赋值给d,不但不丢失精度反而提高了精度。
int i;
i=5.1;//右值5.1为双精度,左值为整型
右值双精度浮点型5.1降低为左值整型,即5.1舍弃小数部分后,把5赋给整型变量i,这种情况会丢失精度。4. 右值超出左值类型范围更糟糕的情况是,赋值运算符右值的范围超出了左值类型的表示范围,将把该右值截断后,赋给左值。所得结果可能毫无意义。例如:
char c; //char占8位,表示范围-127~128
c=1025; //1025=210 1,对应二进制形式: 100 0000 0001,超出了8位
printf(“%d”,c); //以十进制输出c的值
该输出结果为1,因为只取1025低8位0000 0001(值为1),赋给字符型变量c,故得到毫无意义的值。当return后的表达式类型与函数的返回值类型不一致时,也会自动把return后表达式的值转换为函数类型后,再返回。当函数调用时,所传实参与形参类型不一致时,也会把实参自动转换为形参类型后再赋值。后两种类型转换,将在后续章节中讲解。3.9.2强制类型转换虽然自动类型转换不需要人工干预,使用方便,但有利也有弊,尤其当自动类型转换是从较高类型转换为较低类型时,将会降低精度或截断数据,可能得不到预期的结果。为了给程序设计人员提供更多的类型转换控制权限,使程序设计更加灵活,转换的目的更加清晰,C语言提供了可显式指定类型转换的语法支持,通常称之为强制类型转换。强制类型转换的格式为:
(目标类型) 表达式
例如,计算某工厂目前可出厂的产品总件数用total表示,该工厂共有三个车间,已知: 1车间目前完成10.9件,2车间目前完成12.7件,3车间目前完成11.8件。则:
int total;
total=(int)10.9 (int)12.7 (int)11.8;
故total=10 12 11=33,符合题意。而如果采用如下的自动类型转换,其值将为35。
int total=10.9 12.7 11.8;
赋值运算符右端表达式10.9 12.7 11.8=35.4为双精度浮点型,而左值total类型为整型,将35.4自动转换为整数35后赋给total。与题意不符。小结1. 本章主要知识点梳理
本章主要围绕各种运算符展开讲解,如表31所示为本章涉及的各种常见运算符及其对比分析。
表31本章主要知识点梳理
知识点示例说明
前增1与后增1的区别int a=2,b;
(1) b=a ;
表示先把a原值2赋给b,然后a自身加1,变成3。故b=2。
(2) b= a;
表示先把a自身的值加1变成3后,再把a的值3赋给b。故b=3后增1与前增1的区别是,前者先使用变量原值参与运算,然后变量自身值加1; 后者是先把变量原值加1后,用加1后的值再参与运算。
两者相同点: 变量自身都加了1相除/与相除取余%17/3=5//两操作数全为整数
9/2.0=4.5//至少一个操作数为浮点数
17%3=2
9%2.0//编译错误,两操作数都必须为整数/运算符:
当两操作数均为整数时,则结果为相除取整; 当两操作数中至少有一个为浮点数时,结果为浮点数。
%表示相除取余数部分,要求两操作数必须为整数
整数的左移和右移int a=20,b,c,d;
b=a<<2;//b=80
c=a>>2;//b=5
d=a>>3;//相当于20/8取整,d=2把十进制整数左移一位相当于该数乘以2,右移一位,相当于该数除以2取整运算符的优先级int a;
a=1 2×3
由于上述表达式中,含有 、×、=三个运算符,优先级乘号×最高,其次是加号 ,赋值号=最低,故相当于: a=(1 (2×3))当表达式中不同优先级的运算符参与运算时,主要依据运算符的优先级确定其运算顺序续表
知识点示例说明
运算符的结合性1 2 3 4的结合过程为:
(1 2) 3 4–>((1 2) 3) 4
a=b=c=6的结合过程为:
a=b=(c=6)–>a=(b=(c=6))当表达式中多个运算符的优先级相同时,则主要根据运算符的结合性确定其运算顺序逻辑与&&的短路特性int a=0,b=2,c;
c=a&& b;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
输出结果为:
a=0,b=2,c=0逻辑与&&“短路”: 当逻辑与&&的左操作数为逻辑假时,就足以判断该逻辑运算的结果为假了,故右操作数就不再被执行。
由于a=0,即逻辑&&左操作数为假,则无须计算右操作数 b,即可得逻辑运算的结果为假逻辑或‖的短路特性int a=-1,b=2,c;
c=a‖ b;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
输出结果为:
a=-1,b=2,c=1逻辑或‖“短路”: 当逻辑或‖的左操作数为逻辑真时,就足以判断该逻辑运算的结果为真了,故右操作数就不再被执行。
由于a=-1非0,即‖的左操作数为真,已可决定了逻辑或结果为真,故右操作数 b不再被执行
2. 本章易错知识点本章易错知识点见表32。
表32本章常见易错点
易错知识点示例说明
相除取余%操作的运算数非整数7%2.1语法错误,%运算符的两个运算数必须均为整数自增自减运算的操作数非左值(1) 常量自增
#define N 3
N ;//错误,不能对常量自增运算
(2) 表达式自增
int a=2,b=3,c;
c=(a b) ;//错误,表达式不能自增运算自增或自减运算的操作数必须为左值,不能为常量或表达式错误运用/运算符求球的体积
int r=1.5,v;
v=4/3*3.14*r*r*r;
上式并不能得到正确的体积,原因是4/3=1,不是1.333。修改方案:
v=4.0/3*3.14*r*r*r;使用/用作数学中的相除运算时,必须把其中至少一个运算数转换成浮点型数据参与运算。否则如果两操作数均为整型,则结果为商值
习题1. 运算符和表达式中的基本概念
(1) 简述左值和右值的概念。(2) 简述一元、二元和三元运算符的概念及举例。2. 算术运算符及算术表达式(1) 分解十进制整数5678的个位、十位、百位和千位数字。(2) 设有定义语句int a=5,b;,则以下语句中与b=a ;等价的语句是()。
A. a ;b=a;B. a;b=a;C. b=a;a=a 1;D. b=a;a=a;(3) 设int a,b;,则a=2,b=a 7%2的值是()。3. 赋值运算符及赋值表达式(1) 设int a=0,b=1;,则表达式( a‖–b)的值是,a的值是,b的值是。(2) 设int a=3,b=1;,则表达式(a>2‖3&&b–)的值是,a的值是,b的值是。(3) 设int a=0,b=1,c;,则执行语句c=a >=b‖b >1;后,a、b、c的值各为多少?(4) 分析以下程序,输出其运行结果。【程序代码】
#include
int main(void)
{
int a=0,b=1,c;
c= a>=b&&b >1;
printf(“a=%d,b=%d,c=%d\n”,a,b,c);
return 0;
}
(5) 设有int a=5,b=2;,则执行b*=a 5/2;后,b的值是。4. 移位运算符及移位表达式(1) 十进制整数20左移一位后的值是多少?写出计算过程。(2) 十进制整数30右移一位后的值是多少?写出计算过程。5. sizeof运算符及其表达式(1) 执行printf(“%d\n”,sizeof(char));输出结果是。(2) 执行printf(“%d\n”,sizeof(5.1));输出结果是。6. 逗号运算符及逗号表达式(1) 设有定义语句int a=2,b=3;,则表达式(a 2,b=6, b a)的值是。(2) 设有定义语句int t=1;,则执行printf(“%d”,(t 5,t ,t 3));输出结果为。(3) 思考,如果把上题中逗号表达式的括号去掉,即:
int t=1;printf(“%d”,t 5,t ,t 3);
该输出语句规范吗?会出现什么情况?7. 运算符的优先级与结合性(1) 设有int a=5;,执行a =a=a/=a*=a;后a的值是多少?(2) 设有int a=5;,执行a =a/=a*=a=a;后a的值是多少?8. 类型转换1) 自动类型转换(1) 设int a,b;,则a=2,b=a 7/2.0的值是。(2) 设有以下程序段,其运行结果是。
int a;
float b;
b=(a=2 3.1*4,a ,a%4);
printf(“a=%d,b=%f\n”,a,b);
2) 强制类型转换设有以下程序段,其运行结果是。
double a;
int b;
b=(int)(a=2 3.1*4,a/=2,(int)a%4);
printf(“a=%.3f,b=%d\n”,a,b);
![插图](https://static.easterneast.com/file/easternspree/img/60544195f0f22413d6e74415_314939.jpg)
![插图](https://static.easterneast.com/file/easternspree/img/6054419af0f22413d6e74416_314940.jpg)
![插图](https://static.easterneast.com/file/easternspree/img/605441aef0f22413d6e74417_314941.jpg)
![插图](https://static.easterneast.com/file/easternspree/img/605441b2f0f22413d6e74418_314942.jpg)
![插图](https://static.easterneast.com/file/easternspree/img/605441b6f0f22413d6e74419_314943.jpg)
![插图](https://static.easterneast.com/file/easternspree/img/605441bcf0f22413d6e7441a_314944.jpg)
评论
还没有评论。