【《C Primer Plus》读书笔记】第5章:运算符、表达式和语句
5.1 运算符
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符,并提供了以下类型的运算符:
- 算术运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 赋值运算符
- 杂项运算符
算术运算符
下表显示了 C 语言支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:
关系运算符
下表显示了 C 语言支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20,则:
逻辑运算符
下表显示了 C 语言支持的所有关系逻辑运算符。假设变量 A 的值为 1,变量 B 的值为 0,则:
位运算符
位运算符作用于位,并逐位执行操作。&、 | 和 ^ 的真值表如下所示:
假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:
A = 0011 1100
B = 0000 1101
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
赋值运算符
下表列出了 C 语言支持的赋值运算符:
杂项运算符 ↦ sizeof & 三元
下表列出了 C 语言支持的其他一些重要的运算符,包括 sizeof 和 ? :。
sizeof 运算符以字节为单位返回运算对象的大小。C语言规定,sizeof 返回 size_t 类型的值。这是一个无符号整数类型,但它不是新类型。
C 中的运算符优先级
运算符的优先级确定表达式中项的组合。这会影响到一个表达式如何计算。某些运算符比其他运算符有更高的优先级,例如,乘除运算符具有比加减运算符更高的优先级。
下表将按运算符优先级从高到低列出各个运算符,具有较高优先级的运算符出现在表格的上面,具有较低优先级的运算符出现在表格的下面。在表达式中,较高优先级的运算符会优先被计算。
5.2 表达式与语句
表达式
表达式由运算符和运算对象组成。
C 表达式最重要的特性是,每一个表达式都有一个值。
语句
语句是 C 程序的基本构建块。一条语句相当于一条完整的计算机指令。
语句可分为简单语句和复合语句。
C 术语:副作用和序列点
副作用是对数据对象或文件的修改。
序列点是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在 C 语言中,语句中的分号标记了一个序列点,另外,任何一个完整表达式(这个表达式不是另一个更大表达式的子表达式)的结束也是一个序列点。
复合语句(块)
复合语句是用花括号括起来的一条或多条语句。
类型转换
数据有不同的类型,不同类型数据之间进行混合运算时必然涉及到类型的转换问题。
转换的方法有两种:
- 自动转换(隐式转换):遵循一定的规则,由编译系统自动完成
- 强制类型转换:把表达式的运算结果强制转换成所需的数据类型
类型转换的原则:占用内存字节数少(值域小)的类型,向占用内存字节数多(值域大)的类型转换,以保证精度不降低。
在赋值表达式语句中,计算的最终结果会被转换成被赋值变量的类型。这个过程可能会导致类型升级或降级。
当把一个浮点数值转换成一个整数类型时,原来的浮点值会被截断。
例如:
/* 类型降级 */
#include <stdio.h>
#include <stdlib.h>
int main()
{
float a = 23.99;
int b = a;
printf("%d\n", b); // 打印23,精度丢失
system("pause");
return 0;
}
运行结果:
类型的级别从高到底依次是 long double、double、float、unsigned long long、long long、unsigned long、long、unsigned int、int。例外的情况是,当 long 和 int 的大小相同时,unsigned int 比 long 的级别高。
当作为函数参数传递时,char 和 short 被转换成 int,float 被转换为 double。
隐式转换
遵循一定的规则,由编译系统自动完成。
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
float p = 3.14;
int w = 2;
// 隐式转换
float sum = p * w;
printf("%f\n", sum);
system("pause");
return 0;
}
运行结果:
强制类型转换
在某个量的前面放置用圆括号括起来的类型名,该类型名即是希望转换成的目标类型。
圆括号和它括起来的类型名构成了强制类型转换运算符。其通用行式是(type)。
例:
/** 强制类型转换 **/
#include <stdio.h>
#include <stdlib.h>
int main()
{
float p = 4.6;
int w = 2;
int sum = (int)p * w;
printf("%d\n", sum);
sum = (int)(p * w);
printf("%d\n", sum);
system("pause");
return 0;
}
运行结果:
带参数的函数
程序清单5.15:
/* pound.c -- 定义一个带一个参数的函数 */
#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);相同
system("pause");
return 0;
}
void pound(int n) // ANSI风格函数头
{
while (n-- > 0) // 表明该函数接受一个int类型的参数
printf("#");
printf("\n");
}
形参与实参
我们看该程序的函数头:void pound(int n)
声明参数就创建了被称为形式参数的变量,在该例中,形参就是 int 类型的 n。
我们称函数调用传递的值为实际参数,像pound(10)这样的函数调用会把 10 赋给 n。过程为:函数调用pound(10)把实参 10 传递给函数,然后该函数把 10 赋给形参(变量 n)。
变量名是函数私有的,即在函数中定义的函数名不会和别处的相同名称发生冲突。
运行结果:
函数调用
我们看该程序的函数头:void pound(int n)
原型是函数的声明,描述了函数的返回值和参数。
pound() 函数的原型说明了2点:
- void 说明该函数没有返回值
- 该函数有一个 int 类型的参数
该例中,函数原型告诉编译器pound() 需要一个 int 类型的参数。相应地,当编译器执行到 pound(ch)时,会把参数 ch 自动转换成 int 类型。