运算符和表达式
运算符
C语言的运算范围很宽,把除了控制语句和输入/输出以外的几乎所有的基本操作都作为运算符处理。运算符主要分为三大类:算术运算符、关系逻辑运算符、按位运算符。根据运算符的运算对象的个数,C语言的运算符又可分成单目运算符、双目运算符、三目运算符。运算符具体分类情况如下表所示:
分类名称 | 运算符 |
算术运算符 | +、-、*、/、% |
关系运算符 | <、>、<=、>=、==、!= |
逻辑运算符 | &&、||、! |
自增自减运算符 | ++、-- |
位运算符 | <<、>>、~、|、^、& |
赋值运算符 | =、+=、-=、*=、/= |
条件运算符 | ?: |
逗号运算符 | , |
指针运算符 | *、& |
求字节数运算符 | sizeof |
强制类型转换运算符 | () |
分量运算符 | .、-> |
下标运算符 | [] |
其他 | {} |
运算符的结合性
C语言中各种运算符的结合性分成左结合性(自左至右)和右结合性(自右至左)两种。
例如,算数运算符是左结合性,即先左后右的。如:
x-y-z; /* 先执行x-y的操作,再执行-z的操作 */
赋值运算符是右结合性,即先右后左的。如:
x=y=z; /* 先执行y=z的操作,再执行x=的操作 */
一般而言,多数运算符具有左结合性,单目运算符、三目运算符、赋值运算符具有右结合性。
表达式
使用运算符将常量、变量、函数连接在一起,就构成了表达式。
结合上文的运算符,对某些表达式进行讲解:
- 除法表达式(/):若参与运算量均为整形时,结果也为整形,舍去小数;若运算量中有一个实数,则结果为双精度实数。
- 求余表达式(%):求余运算符的运算对象必须是整型数据,求余运算的结果等于两数相除的余数,运算结果的符号与被除数的符号相同。例如:
printf("%d\n", 10 % 3); /* 1 */
printf("%d\n", -10 % 3); /* -1 */
printf("%d\n", 10 % -3); /* 1 */
printf("%d\n", -10 % -3); /* -1 */
- 自增自减表达式(++、--):自增自减运算符可以做前缀运算符,也可以做后缀运算符。
- ++i:i自增1后再参与其他运算;
- --i:i自减1后再参与其他运算;
- i++:i参与其他运算后,i的值再自增1;
- i--:i参与其他运算后,i的值再自减1。例如:
int i = 10;
printf("%d\n", ++i); /* 11 */
printf("%d\n", --i); /* 10 */
printf("%d\n", i++); /* 10 */
printf("%d\n", i--); /* 11 */
自增自减运算符在使用时,需要注意一下几点:
- 运算符的操作对象只能是变量,而不能是常量或者表达式;
- 运算符的优先级高于基本算数运算符,结合性是右结合性(自右向左)。例如:
int i = 5, j = 5, p, q;
p = (i++) + (i++) + (i++);
q = (++j) + (++j) + (++j);
printf("%d,%d,%d,%d", p, q, i, j); /* 15,24,8,8 */
- 赋值表达式(=):赋值表达式是将计算出的表达式的值赋予给左边的变量,具有右结合性。在某些高级语言中,赋值构成了一条语句,称为赋值语句。而在C语言中,把“=”定义成运算符,从而构成赋值表达式,也就是说凡是表达式可以出现的地方都可以出现赋值表达式。例如:
int x, a, b;
x = (a = 3) + (b = 4);
printf("%d", x); /* 7 */
上述语句是合法的。当然,在C语言中也可以组成赋值语句。按照C语言规定,任何表达式在其末尾添加分号就构成语句。
在赋值表达式中,如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即将赋值运算符右边的类型换成左边的类型。具体规定如下:
- 实数赋予整数,舍去小数部分;
- 整数赋予实数,数值不变,但将以浮点形式存放,即增加小数部分(小数部分位0);
- 字符赋予整数,由于字符是1个字节,整数(int)4个字节,故将字符的ASCII码放到整数的最低8位;
- 整数赋予字符,只将最低8位赋予字符。
- 关系逻辑表达式(<、>、==、!=、<=、>=、&&、||、!):判断结果用数字1(成立、真)和0(不成立、假)来表示。
如果表达式中同时出现了算术运算符、关系运算符、赋值运算符等,它们的优先级由高到低,顺序如下:
!——算术运算符——关系运算符——&&和||——赋值运算符
也就是说,逻辑运算符!和&&、||的优先级顺序不同,这里需要注意。
- 逗号表达式(,):把若干个表达式用逗号运算符连接在一起组成一个表达式。逗号运算符的优先级低于赋值运算符,是C语言所有运算符中优先级最低的运算符,是左结合性。它的操作过程是:从左向右以此计算表达式,最终将最右边表达式的值作为逗号表达式的值。例如:
int a = 2, b = 4, c = 6, x, y;
y = (x = a + b), (b + c);
printf("%d,%d", y, x); /* 6,6 */
在这个例子中,由于赋值运算符的优先级较高,也就是:(y = (x = a + b)) , (b + c) ,x=6,y=6;而整个逗号表达式的值是10,但是并没有什么用处(没有把结果赋值给某个变量)。通常,逗号表达式是分别求各表达式的值,而并不一定要求整个逗号表达式的值。
- 位运算表达式(&、|、^、~、<<、>>):位运算只能用于整形操作数,即只能用于带符号或无符号的char、short、int、long类型。
- 异或(^):若参与运算的两个二进制值相同为0,否则为1,且满足交换律、结合律。
利用异或运算交换两个数:
a = a^b;
b = b^a;
a = a^b;
语句
语句是构成程序的基本部分,程序是一系列带有标点的语句集合。在C语言中,这个标点就是分号“;”。
结合上文,对语句进行部分讲解:
- 在变量声明的时候,不允许连续给多个变量赋初值。例如:
int a = b = c = d = 1; /* 错误使用 */
int a,b,c,d;
a = b = c = d = 1; /* 正确使用 */
C语言的流程——程序流程控制结构
程序的流程有顺序结构、选择结构和循环结构3种。
由于这部分知识比较简单,只对其中的某些点进行提及就可以了,就不面面俱到了。
switch语句
switch (变量)
{
case 常量表达式1:语句1; break;
case 常量表达式2:语句2; break;
case 常量表达式3:语句3; break;
case 常量表达式4:语句4; break;
...
default:语句n + 1;
}
关于switch结构的说明如下:
- switch后面的变量类型一般为整型、字符型和枚举型,但不能是浮点型;
- 每个常量表达式的值必须各不相同,没有先后次序。
- 多个case语句可以执行共同的执行语句。如:
switch (x)
{
case 1:
case 2: printf("Hello!"); break;
default: printf("World!");
}
表示x的值无论是1还是2,都会执行printf("Hello!")这条语句。
跳转结构语句
C语言中还有一种可以使程序执行过程中跳转到指定位置的结构,将其称为跳转结构。跳转结构一般在程序中与循环结构或分支结构配合使用,所以也可以认为是为了配合其他结构的。
C语言中提供了3种实现跳转结构的语句:break语句、continue语句、goto语句。
- break:一般可用于switch语句中,可使得程序跳出switch而执行switch以后的语句;还可用于循环语句,可使得程序终止循环而执行循环后面的语句。通常在循环语句中,需要和if连在一起,即满足一定条件时跳出循环;
- continue:一般用于循环语句,可使得程序跳出循环体中剩余的语句而强制直接进入到下一次循环。通常在循环语句中,需要和if连在一起,即满足一定条件时跳出本次循环,直接进行下一轮循环;
- goto:无条件转向语句,即转向到指定语句标号处,执行标号后面的程序。其语法格式为:
goto 语句标号;
其中,语句标号是一个有效的标识符,它的命名规则与变量名的命名规则相同。语句标号加上一个“:”一起出现在程序的某处,执行goto语句后,程序将跳转到该标号处并执行其后的语句。例如:
loop_1 : printf("goto语句跳转到此标号");
goto语句的主要应用:
- 与if语句一起构成循环结构;
- 从循环体内跳转到循环体外,甚至一次跳出多重循环结构(continue和break只能结束本次循环和跳出本层循环)。
goto语句的使用注意点:
- 标号必须与goto语句同处于一个函数中,但可以不在一个循环层中;
- goto语句可以从条件语句或循环语句的里面转移到条件语句或循环语句的外面,但不允许从条件语句或循环语句的外面转移到条件语句或循环语句的里面;
- 由于goto语句破坏了结构化程序单入口、单出口的特点,容易引起程序流程的混乱,一般不主张使用goto语句。