c语言操作符和表达式小结
本篇文章主要简述c语言操作符的一些基本用法,以及一些典型的问题的辨析。
目录
算数操作符
C语言中算数操作符包括:
+ - * / %
值得注意的是,在两个整数除法运算时, / 执行整除运算;在两个浮点数除法运算时, / 进行浮点型除法运算。在进行取模运算时, % 只接受整形数,不接受其他类型的数。
位移操作符
C语言中位移操作符包括:
右移操作符 >> 左移操作符 <<
位移操作符的作用是,讲一个数值在二进制层面上,将它的位进行向左或是向右的移动。向左移动的过程当中,最前面的几位将会被丢弃,而后面将会按照丢弃的位数进行补0。向右移动的过程当中,后面的几位将会被丢弃,前面将会补1(在正数情况下)。右移操作时,若移动的数是负数,将进行逻辑移位,前面回补1。
*使用位移运算符时,向右移动n位,相当于除以 2^n;向左移动n位,相当于乘以2^n。
*位移运算比算数运算更加高效。
*在使用位移运算符时,通常不移动负数,例如:a<<-5。因为编译器没有对这类操作有详细的定义,在使用中应该避免这样用。
位操作符
位操作符有:
按位与 & 按位或 | 按位异或 ^
位操作符的使用是按照位来运算的,拿按位异或来说,上下对其的两个位进行异或运算,相同为0,不同为1。设 a = 0110 1110, b = 0010 1011。
小练习题
给定一个整数,返回这个整数的二进制形式的1的个数。
#include<stdio.h>
#include<process.h>
int main()
{
int value = 0;
int count = 0;
scanf_s("%d", &value);
while (value != 0)
{
if (value < 0)
{
value = -value;
count = count + 1;// 负数的符号位加一
}
if ((value % 2) != 0)// 模2就相当于二进制表达剪掉一位
{
count = count + 1;
}
value = value >> 1;//向右位移一位
}
printf("这个数有%d个1\n", count);
system("pause");
return 0;
}
十进制层面:15 % 2 = 1,7 % 2 = 1, 3 % 2 = 1, 1 % 2 = 1;
二进制层面 : 1111>>1=0111,0111>>1=0011,0011>>1=0001,0001>>1=0000;
赋值运算符
赋值运算符,是将等号=右边的值储存于等号左边的值。
例如: a = b + 1;
这里需要注意的是,不建议进行连续赋值操作。虽然在语法上它是可行的,但是减弱了代码的可读性,使得调试和阅读产生了影响。
例如:r = s + ( t = u - v) / 3 可以拆成: t = u - v; r = s + t / 3;
而后者明显比前者更好理解。
复合运算符
复合运算符有如下:
+= -= *= /= %= <<= >>= &= ^= !=
复合运算符可以减少代码的书写量,可以更好的偷懒,另外编译器也可以产生紧凑的代码。
例如: y+(x/2-10+f(z)) = y+(x/2-10+f(z)) +1; 可写成 y+(x/2-10+f(z)) += 1;
编译器算前者要调用两次f(z)函数,而后者只需要一次f(z).
单目操作符
单目操作符如下:
! ++ -(负号) & sizeof ~ -- +(正号) *(指针) (类型)
* 这里主要说说sizeof,sizeof可以用来判断变量和类型的大小,它的单位是字节(byte)。
当它判断类型时要加括号,例:sizeof(int),判断变量时可以不加括号。
在判断表达式长度的时候,sizeof( a = b + 1);对其整体进行判断,并不对其进行计算。
*(类型)操作符被称之为强制类型转换。如(float)a,这个操作符有很高的优先级,在对表达式使用的时候要用括号括起来,以免产生错误。
* 自增运算符需要注意的问题,++i,表示先加后用, i++,表示先用后加。
#include<stdio.h>
#include<process.h>
int main()
{
int i = 0, a = 0, b = 0;
a = i++;
b = ++i;
printf("a = %d, b = %d\n", a, b);
system("pause");
return 0;
}
关系操作符
关系操作符如下:
> >= < <= != ==
这里需要注意的是:在进行while(),if()判断的时候,将==号错写成=号。
例如: if(x==5) 错写成 if(x=5)
以上在编译器编译的时候并不会报错,但程序的逻辑是错误的,所以建议将其写成:
if ( 5 == x) 数字在前,变量在后。
逻辑操作符
&& ||
逻辑操作符存在一个行为:短路求值
其表述如下:&&操作符的左操作符总是首先进行求值,如果它的值为真,然后就对右操作数进行求值。如果左操作数的值为假,那么右操作数就不必求值,因为整个表达式肯定为假;同理||操作符如果做操作数为真,那么整个表达式就为真。
小练习题
这是一道非常典型的题。
#include<stdio.h>
#include<process.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++&&++b&&d++;
//i = a++||++b||d++;
printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
system("pause");
return 0;
}
程序执行到第二行的时候,碰到a++,这时是先用后加,所以0与右操作数,表达式为假,然后
0&&d++还是假,程序结束。根据短路求值,只有a++执行了操作,b和d都没有执行,所以结果如图所示。请读者朋友自行换掉注释来算得答案。
条件操作符
条件操作符如下:
? :
条件操作符很特殊,它是一个三目操作符。它所表达的是 if……else的关系。
if(a>5)
c[2*b + e(d/5)] = 1;
else
c[2*b + e(d/5)] = 2;
||
c[2*b + e(d/5)] = a > 5 ? 1 : 2;
它也可以减少代码量,少打一次表达式,就少了出错的可能性。
逗号操作符
逗号操作符如下:
代码1 , 代码 2 ,……, 代码 n
逗号表达式使得编译器自左向右,依次处理代码,整个式子的表达式的值依照最后一个逗号为准。
使用逗号操作符可以做到一些骚操作,例如对于条件,和循环语句的简化。
a = f1(x);
b = f2(x + a);
for (c = f3(a, b); c > 0; c = f3(a, b))
{
a = f1(++x);
b = f2(x + a);
}
||
for (c = f3(f1(x), f2(x + a);); c > 0; c = f3(f1(++x), f2(x + a)))
小练习题
下面的程序将打印几次呢?
#include<stdio.h>
#include<process.h>
int main()
{
int i = 0, j = 0;
for (i = 0, j = 0; j = 0; i++, j++)
{
printf("hehe\n");
j++;
}
system("pause");
return 0;
}
答案是零次。因为初始条件中 i = 0 , j = 0,所以最终的判定是最后一个逗号后的表达式。for的终止条件是 j = 0。所以程序不执行。
表达式求值
1.整形提升
在表达式求值的过程中,字符型和短整型操作数在使用之前会被转换为普通的整形。
char a, b, c;
a = b + c;// b,c先被提升为普通整形,然后将计算结果截断,再传给a.
2.优先级顺序
下面这段话是《c和指针》里的:
两个相邻操作符的执行顺序由他们的优先级决定。如果他们的优先级相同,它们的执行顺序由它们的结合性决定。除此之外,编译器可以自由的决定热河顺序表达式进行求值,只要他不违背逗号、&&、||和条件操作符所施加的限制。
小练习题
这段代码会输出什么?
#include<stdio.h>
#include<process.h>
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int a;
a = fun() - fun()*fun();
printf("%d\n", a);
system("pause");
return 0;
}
当初我在这里想的是4 - 2 * 3 = -2;但是实际结果确是 -10。这里或许执行了 2 - 3 * 4 = - 10;
在书上说,不同编译器对函数调用的顺序是不同的,如果它们的执行具有副作用,比如一些I/O任务或是修改全局变量,那么函数顺序调用的不同可能会产生不同结果。
所以这里也可能执行了 2 - 4 * 3 = -10;
谢谢大家在百忙之中,阅读本片文章,这篇文章的大部分内容来自于《c和指针》的学习。
PS:第一个小练习题已修改,原题不能处理负数。