5.1 循环简介
5.2 基本运算符
赋值运算符:=
几个术语:数据对象、左值、右值和操作数
数据对象:泛指数据存储区的术语,数据存储区能用于保存值 。
左值:用于标识一个特定数据对象的名字或表达式。
右值:能赋给可修改的左值的量。右值可以是常量、变量或者任何可以产生一个值的表达式。
操作数:运算符操作的对象。
加法运算符:+
减法运算符:-
符号运算符:-和+
C90标准把一元+运算符加入了C中。没有改变什么,只是使 dozen= +12;这样的语句编译器不报错。这种结构之前是不允许的。
乘法运算符: *
除法运算符:/
浮点类型的除法运算得出一个浮点数结果,而整数除法运算得出一个整数结果 。在C中整数除法运算的小数部分被丢弃,这个过程称为截尾。
计算机不能进行真正的混合运算用整数去除浮点数,所以编译器将两个操作数转变成一致的类型。在这种情况下,做除法运算前将整数转化为浮点数 。
C99标准之前的C语言舍入给了实现者一些空间,负整数除法舍入可以采用“小于或等于该浮点数的最大整数”,C99标准之后不管正整数还是负整数除法必须采用丢弃小数部分的“趋零截尾” 。
运算符的优先级
+/-有两种用法,一种是作为二元运算符,还有作为一元运算符。两种用法优先级不同,一元运算符的用法优先级较高 。
优先级和求值顺序
运算符的优先级为表达式的求值顺序提供了重要的规则,但是它并不决定所有的顺序 。如果一个表达式出现了两个相同优先级的运算符,先运算哪个由实现者编译器决定,可能在一种硬件上先算左边的效率高,但到了另一种硬件上确实先算右边的效率高。
5.3 其他运算符
sizeof 运算符和size_t类型
sizeof 是C语言的内置运算符,不是宏,属于语言标准的一部分,而非某个头文件或库,类似 (+、-),其行为由编译器直接实现,无需外部定义 。
C规定size_of返回size_t类型的值。这是一个无符号整数类型,但不是新类型。size_t类型是通过关键字typedef 定义的一个基本类型的别名。我通过查询编译器中的stdio.h文件包含的crtdefs.h文件得知,我的编译器中的size_t类型定义是typedef unsigned int size_t; 。
取模运算符:%
取模运算符用于整数运算。不要对浮点数用该运算符,那是无效的 。
负数的取模运算的规则:如果第一个操作数为负数,那么得到的模也是负数;如果第一个操作数是正数,那么得到的模也是正数 。
增量和减量运算符:++和–
增量和减量运算符以两种方式出现:第一种方式,运算符出现在它作用的变量前,这是前缀模式 ;第二种方式,运算符出现在它作用的变量后,这是后缀模式 。
这两种方式的区别在于值得自增或自减这一动作发生得准确时间不同 。单独使用增量或减量运算符时,使用哪种都没关系,值是相同的。当运算符及其操作数是一个更大的表达式一部分的时候,选择哪种方式就很重要。前缀模式先将运算符的操作数自增或自减再参与更大表达式其他部分的运算;后缀模式先将运算符的操作数参与更大表达式其他部分的运算再自增或自减
。
增量和减量运算符有很高的结合优先级,只有圆括号比它们的优先级高 。
在C中,编译器可以选择先计算函数里哪个参数值。这个自由提高了编译器的效率,但是如果在函数参数里使用了增量运算符就会带来麻烦 。通过如下原则,可以很容易的避免这些问题。
如果一个变量出现在一个函数的多个参数中,不要将增量或减量运算符应用在该变量上
。
如果一个变量多次出现在同一个表达式里,不要将增量或减量运算符应该在该变量上
。
5.4 表达式和语句
表达式
表达式是由运算符和操作数组合构成的 。最简单的表达式是一个单独的操作数。C中的一个重要属性每个表达式都有一个值 。包括赋值运算符的表达式,比如表达式age=30作为一个整体的值就是30。
语句
语句是构造程序的基本成分。程序是一系列带有某种必需的标点的语句集合 。在C中,语句用结束处的一个分号标识,一个语句是一个完整的计算机指令
。
C把任何后面加一个分号的表达式看作是一个语句,它们被称为表达式语句。
副作用和顺序点
副作用是对数据对象或文件的修改 。state=50;的副作用是将变量state的值设置为50。看起来更像主要目的,然而,从C的角度看,主要目的是对表达式求值。C计算表达式state=50的值50,副作用把变量state的值改变为50。
一个顺序点是程序执行中的一点;在该点处,所有的副作用都在进入下一步前被计算 。 在C中,语句里的分号标志了一个顺序点。任何一个完整的表达式结束也是一个顺序点
。一个表达式不是一个更大的表达式的子表达式,那就是完整表达式。
复合语句(代码块)
5.5 类型转换
类型转换的基本规则:
当出现在表达式里时,有符号和无符号的char和short类型都被自动转换为int ,在需要的情况下,将被自动转换为unsigned int。因为是转换成较大的类型,所以这些转换被称为提升。
在包含两种数据类型的任何运算里,两个值都被转换成两种类型里较高的级别 。不是分别转成两种类型的较高级别,而是看两种类型哪个级别较高,将低的转换成高的级别,统一类型再计算。
在赋值语句里,计算的最后结果被转换成将要被赋值的那个变量的类型。这个过程可能导致提升,但也可能导致降级 ,降级是将一个值转换成更低级的类型。
当作为函数的参数传递时,char和short会被转换为int,float会被转换成double 。后续第9章会学到 可以通过函数原型阻止自动提升的发生
。
提升通常是一个平滑,无损害的过程,但是降级可能导致真正的问题。原因就是:一个较低级别的类型不够大,不能存放一个完整的数。
指派运算
指派:在某个量的前面放置用圆括号括起来的被希望转换成的类型名。圆括号和类型名一起构成了指派运算符,形式如下:(type)
5.6 带有参数的函数
5.7 一个实例程序
5.11 编程练习
这章还是比较简单的,就最后一个编程练习困住我了,困住我的原因就是上一章节的只是忽略了一些细节:
(1)printf()和scanf()都有控制字符串,控制字符串中的转换说明符和修饰符大部分都是通用的,但还是一些区别,我这次就忽略了, float和double在通过printf()打印时,因为float会自动提升,所以不区分,都是%f转换说明符打印。但是通过scanf()函数读取时要区分float和double,因为在内存中float占4字节用%f,double占8字节用%lf,不区分会导致数据截断引发错误 。注意%Lf代表long double类型,不是double类型的另一种写法。
(2)解决需求“当用户输入q或其他非数字值时,循环结束”时,冥思苦想找不到方法,最后无奈百度找答案,看到答案时感觉自己真实学了就忘。 解决方法就是利用scanf()函数的返回值。printf()和scanf()这两个函数有返回值的,等学到对文件进行操作时应该会经常用到它们的返回值来判断读写文件的情况 。可能这就是写代码的意义吧,再简单的代码,写出来就会发现很多自己忽视的细节。
#include <stdio.h>
#include <windows.h>
void temperatures(double);
int main(void)
{
SetConsoleOutputCP(65001); // 设置为UTF-8 我用的Editplus写代码,需要调用此函数让控制台能支持中文
double db;
printf("请输入一个华氏温度值:_____\b\b\b\b\b");
while (scanf("%lf",&db))
{
temperatures(db);
printf("\n请输入一个华氏温度值:_____\b\b\b\b\b");
}
return 0;
}
void temperatures(double fah)
{
double cel; //摄氏温度
double kel; //绝对温度
const double c_1 = 1.8;
const double c_2 = 32.0;
const double k_1 = 273.16;
cel = c_1*fah+c_2;
kel = cel+k_1;
printf("\n华氏温度%.2f = 摄氏温度%.2f = 绝对温度%.2f\n",fah,cel,kel);
}