第5章 运算符、表达式和语句

学习笔记——《C Primer Plus》

5.1 循环简介

while 循环

#include <stdio.h> 
#define ADJUST 7.31    //character constant
int main(void)
{
	const double SCALE = 0.333;  //const variable
	double shoe, foot;
	
	printf("Shoe size (men's') foot lenght\n");
	shoe = 3.0;
	while(shoe < 18.5)         //while循环开始 
	{
		foot = SCALE * shoe + ADJUST;
		printf("%10.1f %15.2f inches\n",shoe, foot);
		shoe = shoe + 1.0;
	}
	printf("If the shoe fits, wear it.\n"); 
	
	return 0;
}

当程序第 1 次到达 while 循环时,会检查圆括号中的条件是否为真。
该程序中,条件表达式为:
shoe < 18.5
该条件为真,程序进入块中继续执行,把尺码转换成英寸;
然后打印计算的结果;
下一条语句把 shoe 增加 1.0 ,使 shoe 的值为 4.0:
shoe = shoe + 1.0;
此时程序返回 while 入口部分检查条件。

为何要返回 while 的入口部分呢?
因为上面这条语句的下面是右花括号,代码使用的是一对花括号( { } )来标出 while 循环的范围。
花括号之间的内容就是要被重复执行的内容。
花括号以及被花括号括起来的部分被称为块(block)。

当出现条件为假:
shoe < 18.5
控制转到紧跟 while 循环后面的第 1 条语句。

5.2 运算符

5.2.1 基本运算符

赋值运算符:=
加法运算符:+
减法运算符:-

乘法运算符:* (可以使用乘法来计算平方)
例子1:

#include <stdio.h> 
int main(void)
{
	int num = 1;
	
	while(num < 21)
	{
		printf("%4d %6d\n",num , num * num);
		num = num +1;
	}
	
	return 0;
}

例子2:

/*
一位强大的统治者想奖励做出突出贡献的学者。他问这位学者想要什么,
学者指着棋盘说,在第一个方格里放1粒小麦、第2个方格里放2粒小麦、
第3个方格里放4个小麦,第4个方格里放8个小麦,以此例推,棋盘一共有64个方格;
程序计算出每个方格应该多少小麦,并计算总数。 
*/ 

#include <stdio.h> 
#define SQUARES 64  //棋盘中的方格数
int main(void) 
{
	const double CROP = 2E16;  //世界小麦年产谷粒数
	double current, total;
	int count=1;   //循环计数器 
	
	printf("square   grains   total   "); 
	printf("fraction of \n");
	printf("         added    grains  ");
	printf("world total\n");
	
	total = current = 1.0;   //从1颗谷粒开始
	printf("%4d %13.2e %12.2e %12.2e\n",count,current,total,total/CROP); 
	
	while(count < SQUARES)
	{
		count += 1;
		current = 2.0*current;    //下一个方格谷粒翻倍
		total = total = current;  //更新总数
		printf("%4d %13.2e %12.2e %12.2e\n",count,current,total,total/CROP);  
	}
	printf("That's all.\n'"); 
	
	return 0;
}

除法运算符:/
整数除法的结果是整数,整数除法结果的小数部分被丢弃,不会四合五入,这一过程称为截取(truncation);
浮点数除法的结果是浮点数;
整数和浮点数计算的结果是浮点数。
实际上,计算机不能真正用浮点数除以整数,编译器会把两个运算对象转换成相同的类型;在进行除法运算之前,整数会别转换成浮点数。

5.2.2 求模运算符:%

  • 求模运算符(modulus operator)只能用于整数运算,不能用于浮点数。
  • 求模运算符给出其左侧整数除以右侧整数的余数(remainder);例如,13 % 5 (读作13求模5)得 3 。
  • 如果第 1 个运算对象是负数,那么求模的结果为负数;如果第 1 个运算对象是正数,那么求模的结果也是整数。
  • 求模运算符常用于控制程序流。

5.2.3 递增运算符:++

递增运算符(increment operator)执行简单的任务,将其运算对象递增 1 。
该运算符以两种方式出现:前缀模式,++出现在其作用的变量前面;后缀模式:++出现在其作用的变量后面。

#include <stdio.h> 
#define ADJUST 7.31    //character constant
int main(void)
{
	const double SCALE = 0.333;  //const variable
	double shoe, foot;
	
	printf("Shoe size (men's') foot lenght\n");
	shoe = 2.0;
	while(++shoe < 18.5)         //把变量的递增过程放入while循环的条件中。
	{
		foot = SCALE * shoe + ADJUST;
		printf("%10.1f %15.2f inches\n",shoe, foot);
	}
/*	while(shoe < 18.5)         
	{
		foot = SCALE * shoe + ADJUST;
		printf("%10.1f %15.2f inches\n",shoe, foot);
		shoe = shoe + 1;   // 次句等同 : shoe++; 
	}
*/	

	
	
	printf("If the shoe fits, wear it.\n"); 
	
	return 0;
}

shoe的值递增 1 ,然后和 18.5 作比较。如果递增后的值小于 18.5 ,则执行花括号内的语句一次。然后, shoe的值再递增 1 ,重复刚才的步骤,直到 shoe 的值不小于 18.5 为止。

注意:我们把 shoe 的初始值从 3.0 改为 2.0 ,因为在对 foot 第 1 次求值之前, shoe 已经递增了 1 。

while 循环执行一次过程:
在这里插入图片描述

区分前缀和后缀:

#include <stdio.h> 
int main(void)
{
	int a = 1, b = 1;
	int a_post, pre_b;
	
	a_post = a++;  //后缀递增:使用 a 的值之后,递增 a
	pre_b = b++;   //前缀递增:使用 b 的值之前,递增 b
	printf("a   a_post  b   pre_b\n"); 
	printf("%1d %5d %5d %5d\n", a, a_post, b, pre_b);
	
	return 0;
}

执行结果:
在这里插入图片描述
a 和 b 都递增了 1 ,但是,a_post 是 a 递增之前的值,而 pre_b 是 b 递增之后的值。
在这里插入图片描述

(1)单独使用递增运算符(如,ego++;),使用哪种形式都没关系。但是,当运算符和运算对象是更复杂表达式的一部分时,使用前缀或后缀的效果不同。
(2)如果使用前缀形式和后缀形式会对代码产生不同的影响,那么最为明智的是不要那样使用它们。例如,不要使用下面语句:
b = ++i ; // 如果使用 i++ ,会得到不同的结果
应该使用下列语句:
++i;
b = i; //如果第一行使用 i++,并不会影响 b 的值

5.2.4 递减运算符:- -

递增运算符和递减运算符都有很高的结合优先级,只有圆括号的优先级比它们高。
递增和递减运算符只能影响一个变量(或者,更普遍地说,只能影响一个可修改的左值),比如:
x * y- - 表示的是 (x) * (y- -),而不是 (x * y)++;后者无效。

5.3 表达式和语句

5.3.1 表达式

表达式(expression)由运算符和运算对象组成。

每个表达式都有一个值
C 表达式的一个最重要的特性是,每个表达式都有一个值,要获得这个值,必须根据运算符优先级规定的顺序来执行操作。比如,表达式 q = 5*2 作为一个整体的值是10。又如表达式 q > 3 这种关系表达式的值不是0就是1,如果条件为真,表达式的值为1;如果条件为假,表达式的值为0.

5.3.2 语句

语句(statement)是 C 程序的基本构建块。一条语句相当于一条完整的计算机指令。在 C 中,大部分语句都以分号结尾。

5.3.3 复合语句(块)

复合语句(compound statement)是用花括号括起来的一条或多条语句,复合语句也称为块(block)。

/*程序段 1 */
index = 0;
while(index++ < 10)
	sam = 10 * index +2;
printf("sam = %d\n", sam);
/*程序段 2 */
index = 0;
while(index++ < 10)
{
	sam = 10 * index + 2;
	printf("sam = %d\n", sam);
}

(1)程序段 1 ,while循环中只有一条赋值表达式语句。没有花括号,while 语句从 while 这行运行至下一个分号。循环结束后,printf() 函数只会被调用一次。
(2)程序段 2 ,花括号确保两条语句都是 while 循环的一部分,每执行一次循环就调用一次 printf() 函数。根据 while 语句的结构,整个复合语句被视为一条语句,如下图:
在这里插入图片描述

5.4 类型转换

  1. 升级(promotion):从较小类型转换为较大类型。
  2. 降级(demotion):把一种类型转换成更低级别的类型。
  3. 涉及两种类型的运算,两个值会被分别转换成两种类型的更高级别。
  4. 类型的级别从高到低依次是:long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。而 short 和 char 类型会被升级为 int 或 unsigned int。
  5. 在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能导致类型升级或降级。

5.4.1 强制转换

通常,应该避免自动类型转换,尤其是类型降级。
然而,有时需要进行精确的类型转换,或者在程序中表明类型转换的意图。这种情况下要用到强制类型转换(cast),即在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。
圆括号和它括起来的类型名构成了强制转换运算符(cast operator),通用形式是:
(type)

考虑下面两行代码,其中 mice 是 int 类型的变量。第 2 行包含两次 int 强制类型转化。

mice = 1.6 + 1.7;
mice = (int)1.6 + (int)1.7;

第 1 行使用自动类型转换。首先,1.6 和 1.7 相加得 3.3。然后,为了匹配 int 类型的变量,3.3 被类型转换截断为整数 3。
第 2 行,1.6 和 1.7 在相加之前就被转换成整数 1,所以 1+1 的和赋值给变量 mice。
本质上,两种类型转换都好不到哪去,要考虑程序的具体情况再做取舍。一般而言,不应该混合使用类型。

5.5 带参数的函数

#include <stdio.h> 
void pound(int n);  //ANSI 函数原型声明
int main(void) 
{
	int times = 5;
	char ch = '!';  // ASCII 码是33
	float f = 6.0f;
	
	pound(times);   //int 类型的参数
	pound(ch);      //和pound((int)ch);相同
	pound(f);       //和pound((int)f);相同
	
	return 0;
}
void pound(int n)  //ANSI风格函数头,表名函数接受一个 int 类型参数 
{   
	while(n-- > 0) 
	{
		printf("#");
	}
	printf("\n");
}

(1):

  • 声明参数就创建被称为形式参数 (formal parameter) 的变量。
  • 该例中,形式参数是 int 类型的变量 n 。像 pound(10) 这样的函数调用会把 10 赋值给 n 。在该程序中,调用 pound(times) 就是把 times 的值 5 赋值给 n 。
  • 我们称函数调用传递的值为实际参数(actual argumentr),简称实参。
  • 所以,函数调用 pound(10) 把实际参数 10 传递给函数,然后该函数把 10 赋给形式参数(变量 n )。也就是说,main() 中的变量 times 的值被拷贝给 pound() 中的变量 n 。

我们可以说形参是变量,实参是函数调用提供的值,实参被赋值给相应的形参。
因此, times 是 pound() 的实参,n 是 pound() 的形参。

注意:
变量名是函数私有的,即在函数中定义的函数名不会和别处的相同名称发生冲突。如果在 pound() 中用 times 代替 n , 那么这个 times 和 main() 中的 times 不同。也就是说,程序中出现了两个同名的变量,但是程序可以区分它们。

(2):

函数原型即是函数的声明,描述了函数的返回值和参数。
pound() 函数的原型说明了两点:

  • 该函数没有返回值(函数名前面有 void 关键字);
  • 该函数有一个 int 类型的参数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值