本章将会学习 C 语言的运算符、表达式和语句。并且将会介绍选择和循环结构的使用。欢迎大家一起学习。上章入口:【C 语言学习 第三章 格式化输入/输出(I/O) 】
-
1. 运算符和表达式
-
1.1 运算符
-
基本运算符
-
C 使用运算符(operator)来代表算术运算。
-
几个术语:数据对象、左值、右值、和操作数
“数据对象”(data object)是泛指数据存储区的术语,数据存储区能用于保存值。例如,用于保存变量或数组的数据存储区是一个数据对象。
C 的术语左值 (Ivalue)指用于标识一个特定的数据对象的名字或表达式。例如,变量的名字是一个左值。所以对象指的是实际的数据存储,但是左值是用于识别或定位那个存储的标识符。
术语“右值”(rvalue)指的是能赋给可修改的左值的量。
在您学习事物的名称时,我们称之为“项目”的东西(比如在“符号=左边的项目”中的“项目”)的正确术语是“操作数”(operand)。操作数是运算符操作的对象。
1.加、减法运算符:+、-
“加法运算符”(addition operator)使得在它两侧的值被加到一起。
“减法运算符”(subtraction operator)从它前面的数中减去它后面的数。
这时,这两个运算符被称为二元(binary)或双值(dyadic)运算符,这代表它们需要两个操作数。
2.符号运算符:- 和 +
负号可以用于指示或改变一个值的代数符号。正号不改变它知道操作数的值或符号。
这时,这两个运算符被称为一元运算符(unary operator),这代表它们只需要一个操作数。
3.乘法运算符:*
乘法由符号*表示。
4.除法运算符:/
除法由符号/表示。/左边的值被右边的值除。
整数型的除法运算和浮点型的除法运算有很大不同。浮点类型的除法运算得出一个浮点数结果,而整数除法运算则产生一个整数结果。整数不能有小数部分,这使得用3去除5很让人头痛,因为结果有小数部分。在C中,整数除法结果的小数部分都被丢弃。这个过程被称截尾(truncation)。例如:a=3/5; b=3.0/5.0;
结果是,a = 0 ,b = 0.6 。同时,在截尾的过程中是“趋零截尾”,即 -3.4 的截尾是 -3 ,靠近 0 的。
5.取模运算符:%
取模运算符(modulus operator)用于整数运算。该运算符计算出它右边的整数去除它左边的整数得到的余数。6.赋值运算符
在 C 里,符号 = 不代表“相等”,而是一个赋值运算符。比如:a=2024;
相当于把 2024 的值赋给 a ;而
2024=a;
是不正确的。
同时,符号 = 可以和上述几个符号连用。比如:+=、-=、/=、*=、%= 。
以 += 举例,a+=b 相当于 a=a+b 。类似的其他的一样。 -
位运算符
- 任何数据在计算机存储器内都是以二进制数的形式存在的。
- 1.按位与运算符:&
按位与运算是对两个操作数逐位“求与”,当它们都为 1 时,结果为 1 ,否则为 0 。例如:
(10010011)&(00111101)的结果值是:
(00010001)。
2.按位或运算符:|
按位或运算符是对两个操作数逐位“求或”,当它们都为 0 时,结果为 0 ,否则为 1 。例如:
(10010011)&(00111101)
的结果值是:
(10111111)
3.按位异或运算符:^
按位异或运算符是对两个操作数逐位“求异或”,当它们不同时为 1 ,相同时为 0 。例如:
(10010011)&(00111101)
的结果是:
(10101110)
4.按位取反运算符:~
按位取反运算符是对一个操作数逐位“取反”,当为 1 时变为 0 , 当为 0 时变为 1 。例如:
~(10011010)
的结果是:
(01100101)
5.二进制左移运算符:<<
二进制左移运算符是将数据向左移动若干位,移出左边界的所有位丢失,右侧新增加的位为 0 。例如:
(00000001)<< 2
结果是:
(00000100)
6.二进制右移运算符:>>
二进制右移运算符是将数据向右移动若干位,移出右边界的所有位丢失,左侧新增加的位的补充遵循下面的规则:
1)对于无符号数,右移时左侧的新位一律补 0 ,称为“逻辑右移”。
2)对于有符号数,若符号位是 0,则左侧新位一律补 0 ;若符号位是 1 ,则左侧新位一律补 1 ,称为“算数右移”。
例如:
(00001000)>> 2
的结果是:
(00000010) -
其他运算符
- 1.sizeof 运算符
sizeof 运算符以字节位单位返回其操作数的大小。操作数可以是一个具体的数据对象(例如一个变量名),或者一个类型。如果它是一个类型(如 float ),操作数必须被括在圆括号里。
2.增量和减量运算符:++ 和 --
增量运算符(increment operator)完成简单的任务,即将其操作数的值增加 1 。这是以两种方式出现的。在第一种方式中,++ 出现在它作用的变量的前面,这是前缀(prefix)模式。在第二种方式中,++ 出现在它作用的变量的后面,这是后缀(postfix)模式。这两种模式的区别在与值的增加这一动作发生的准确时间是不同的。例如:
程序 1:#include<stdio.h> int main(void) { int a=0,b=0; a++; ++b; printf("%d %d\n",a,b); printf("%d %d\n",a++,++b); printf("%d %d\n",a,b); return 0; }
结果是:
1 1
1 2
2 2
显然,前置 ++ 是先 +1 后使用,而后置 ++ 是先使用后 +1。
类似的,减量运算符(decrement operator)与增量运算符对应。
3.逻辑运算符:&&、||、!
&& 运算符是对两个操作数进行“逻辑与”,即同为真时才为真,其他为假。
|| 运算符是对两个操作数进行“逻辑或”,即同为假时才为假,其他为真。
!运算符是对一个操作数进行“逻辑非”,即假时变为真,真时变为假。
在 C 语言中,非 0 值的表达式视为逻辑真,0 值视为逻辑假。
补充:
当使用 && 或 || 时,若前一个操作数就能够判断出结果时,后一个操作数不进行运算。例如:#include<stdio.h> int main(void) { int a = 0, b = 0, c = 0; c = a++ && b++; printf("%d %d %d\n", a, b, c); c = ++b && a++; printf("%d %d %d\n", a, b, c); c = a-- || b--; printf("%d %d %d\n", a, b, c); c = --b || a--; printf("%d %d %d\n", a, b, c); return 0; }
结果是:
1 0 0
2 1 1
1 1 1
0 0 1
4.关系运算符:<、<=、==、>=、>、!=
<运算符是对两个操作数进行“小于”关系判断。
<=运算符是对两个操作数进行“小于或等于”关系判断。
==运算符是对两个操作数进行“等于”关系判断。
>=运算符是对两个操作数进行“大于或等于”关系判断。
>运算符是对两个操作数进行“大于”关系判断。
!=运算符是对两个操作数进行“不等于”关系判断。 -
1.2 表达式和语句
-
表达式
- 表达式(expression)是由运算符和操作数组合构成的。最简单的表达式是一个单独的操作数,以此作为基础可以建立复杂的表达式。而每一个表达式都有一个值。
-
语句
- 语句(statement)是构造程序的基本成分。程序(program)是一系列带有某种必需的标点的语句集合。
一个语句是一条完整的计算机指令。在 C 中,语句用结束处的一个分号标识。
语句有简单语句和复合语句。
简单语句(simple statement)以一个分号结束。
复合语句(compound statement)是使用花括号组织起来的两个或更多的语句;它也被称为一个代码块(block)。
-
-
2. 运算符的混合运算
-
2.1 自动类转换
- 语句和表达式通常应该只使用一种类型的变量和常量。然而,如果您混合使用类型,C 不会像 Pascal 那样停在那里死掉。相反,它使用一个规则集合来自动完成类型转换。这可能很方便,但是它也很危险,尤其是在您无意地混合使用类型的情况下(许多 UNIX 系统都有自带的 lint 程序可以检查类型“冲突”。如果您选择了一个更高的错误等级,许多非 UNIX 的 C 编译器将报告可能的类型问题)。您最好能有一些类型转换规则的知识。基本的规则如下:
1. 当出现在表达式里时,有符号和无符号的 char 和 short 类型都将自动被转换为 int,在需要的情况下,将自动被转换为 unsigned int(如果 short 与 int 有相同的大小,那么 unsigned short 比 int大:在那种情况下,将把 unsigned short 转换为 unsigned int)。在 K&RC 下,但不是当前的 C 下,float 将被自动转换为 double 。因为是转换成较大的类型,所以这些转换被称为提升(promotion).
2. 在包含两种数据类型的任何运算里,两个值都被转换成两种类型里较高的级别。
3.类型级别从高到低的顺序是 long double 、double 、float 、unsigned long long 、long long 、unsignedIong 、Iong 、unsigned int 和 int 。一个可能的例外是当 long 和 int 具有相同大小时,此时 unsigned int 比 long 的级别更高。
4.在赋值语句里,计算的最后结果被转换成将要被赋予值的那个变量的类型。像规则 1 中一样,这个过程可能㝵致提升;但也可能㝵致降级(demotion),降级是将一个值转换成一个更低级的类型。
5. 当作为函数的参数被传递时,char 和 short 会被转换为 int , float 会被转換为 double。可以通过函数原型来阻止自动提升的发生。
当然在 C 语言中可以强制类型转换:
表达式为: (类型名) 表达式 -
2.2 运算符的优先级
- 像数学一般,C 语言也有运算符的优先级,如下表所示:
-
-
3. 循环语句和选择语句
-
3.1 循环语句
- 循环结构是在给定条件下,反复执行某个语句,这个语句叫循环语句。 C 语言中有三个循环语句:for 循环、while 循环、do while 循环。
-
while 循环
- 结构样式:
while(表达式 1)
{
循环体;
} - 说明:
1)作为循环条件的表达式 1 中一般至少包括一个能够改变表达式的变量,这个变量称为循环变量。
2)当条件表达式为真时,执行循环体,为假时退出循环。while(1) 表示无限循环
3)当循环体不需要实现任何功能时,可以用空语句作为循环体。while((ch=getchar())!='\n');
4)循环语句应有出口(通过循环语句的条件或循环体中的 break 语句)。
5)对于循环变量的初始化应在 while() 语句之前进行,通过适当的方式给循环变量赋初值。 -
程序 1:
/*0~99 的累加*/ #include<stdio.h> int main(void) { int i = 0,s=0; while (i < 100) { s += i; i++; } printf("%d", s); return 0; }
结果:
4950 -
for 循环
- 结构样式:
for(表达式 1 ;表达式 2 ;表达式 3 )
{
循环体
} - 其中,表达式 1 用于进入循环前给某些变量赋初值;表达式 2 表明循环的条件;表达式 3 用于循环,并依次对某些变量的值进行修改。
- for 循环通常用于构造“初值、终值、步长”型循环。
- 注意:
1)表达式 1 、表达式 2 、表达式 3 可以全部或部分省略,但分号是不能省略的。for(;;) 等同于 for(;1;)。
2)循环体也可以空语句,如:for(int i=0;i<100;i++); 或 for(int i=0;i<100;i++) {}
3)不可在循环体中修改循环变量,防止失控。
4)建议 for 语句的循环控制变量的取值采用“半开半闭区间”写法。如上。 -
程序 2:
/* 0~99 的累加*/ #include<stdio.h> int main(void) { int i = 0,s=0; for (i = 0; i < 100; i++) { s += i; } printf("%d", s); return 0; }
结果:
4950 -
do-while 循环
- 结构样式:
do
{
循环体;
}while(表达式 1); - 其中,表达式 1 表明循环的条件。
- 1)while 、do-while 、for 循环是可以相互转换的。但是一般而言,do-while 循环比 for 循环要用得少得多。
2)do-while 循环体类型于 while 循环,不同之处是,它们执行循环体于计算表达式的先后顺序不同。do-while 循环要至少执行一次循环体。
3)与 while 循环和 for 循环不同,do-while 循环在最后要加一个分号,而这往往容易忘记。 - 程序 3:
/* 0~99 的累加*/ #include<stdio.h> int main(void) { int i = 0,s=0; do { s += i; i++; } while (i < 100); printf("%d", s); return 0; }
结果:
4950 -
3.2 选择语句
-
if 语句
- 结构样式 1:
if(表达式 1 )
{
语句 1;
} - 结构样式 2:
if(表达式 1 )
{
语句 1;
}
else
{
语句 2;
} - 结构样式 3:
if(表达式 1 )
{
语句 1;
}
else if(表达式 2)
{
语句 2;
}
......
else
{
语句 n;
} - 以结构样式 3 来说明,C 语言从上往下开始读取,若表达式 1 为真则执行语句 1 且结束该 if 语句,若为假则跳过语句 1 去判断表达式 2 ,真则执行语句 2 且结束该 if 语句,假则跳过继续往下判断,…… ,若以上均为假则执行 else 中的语句 n 结束该 if 语句。
- 程序 4:
#include<stdio.h> int main(void) { char s; s = getchar(); if (s <= '9' && s >= '0') printf("数字\n"); else if (s <= 'z' && s >= 'a' || s <= 'Z' && s >= 'A') printf("字母\n"); else printf("go away!\n"); return 0; }
结果:
b
字母 -
switch 语句
- 结构样式:
switch(表达式 1)
{
case n1:语句 1;
break;
case n2:语句 2;
break;
......
default:语句 n;
break;
} - 当其整数值等于一个数 nk 时,则执行 case nk 所对应的语句 k 且结束 switch 语句。若均不满足,则执行 default 所对应的语句 n 结束 switch 语句。
- 注意:
1)各个 case 和 default 的顺序可以是任意的,但各个 case 后的值必须不一样。
2)表达式 1 的结果必须为一个整数值且 case 后的 n1、n2、... 、n 均为常数。
3)case 语句往往配合 break 语句一起使用,如果不加 break 则程序会按顺序执行下一个 case 语句。
4)default 语句不是必需的。 - 程序 5:
#include<stdio.h> int main(void) { char s; s = getchar(); switch (s) { case 'y': printf("yes\n"); break; case 'n': printf("no\n"); break; default: printf("go away!\n"); break; } return 0; }
结果:
s
go away! -
条件运算符 ? :
- 结构样式:
表达式 1?表达式 2:表达式 3; - 若表达式 1 为真则执行表达式 2 ,否则执行表达式 3 。
- 这往往用于简单的条件选择应用。
- 程序 6:
#include<stdio.h> int main(void) { int a = 3, b = 6; printf("%d", a > b ? a : b); return 0; }
结果:
6
-
-
4. 辅助控制语句
-
4.1 break
- break 语句不能用于循环体语句和 switch 语句之外的任何其他语句。
1)可以使流程跳出 switch 结构,继续执行 switch 下面的语句。
2)可以用来从循环体内跳出循环体,即结束当前循环,执行循环下面的语句。
注意:一个 break 语句只能跳出一层循环。 -
4.2 continue
- 结束本次循环,即跳过循环体尚未执行的语句,接着继续下一次是否执行循环的判定。
continue 语句和 break 语句的区别: continue 语句只是结束本次循环,而不是中止整个循环,而 break 语句则是结束整个循环过程,不在判断执行循环的条件是否成立。 - 程序 7:
#include<stdio.h> #include<math.h> #define PI 3.1415926 int main(void) { double r, area; while (1) { printf("input the radius:"); scanf("%lf", &r); if (fabs(r) <= 1e-5) break; else if (r < 0.0) { printf("the input is error\n"); continue; } area = PI * r * r; printf("the area is:%lf\n", area); } return 0; }
结果:
input the radius:-1
the input is error
input the radius:1
the area is:3.141593
input the radius:0 -
4.3 goto
- 结构样式:
goto 标号;
…
标号:语句; - goto 语句的功能是,把程序控制转移到标号指定的语句处。即执行 goto 语句后,程序从指定标号处继续执行。
注意:goto 语句常用来退出多重循环。但是本章不建议在学习中使用 goto 语句,因为使用 goto 语句很容易出现各种问题,解决起来会非常麻烦。 - 程序 8:
#include<stdio.h> #include<math.h> #define PI 3.1415926 int main(void) { double r, area; while (1) { printf("input the radius:"); scanf("%lf", &r); if (fabs(r) <= 1e-5) goto note; else if (r < 0.0) { printf("the input is error\n"); continue; } area = PI * r * r; printf("the area is:%lf\n", area); } note: return 0; }
结果:
input the radius:-1
the input is error
input the radius:1
the area is:3.141593
input the radius:0
-
参考书籍:《C Primer Plus》【美】 Stephen Prata 著
《程序设计教程 用 C/C++ 语言编程》 周纯杰 何顶新 周凯波 彭刚 张惕远 编著