运算符与表达式
什么是运算符?这里举一个例子:
int x,y //这是声明两个变量
x+y x-y x==y x=y
上面的“+、-、==、=”就是运算符,还有很多其他的运算符,下面我们会一一介绍。
什么是表达式:
(x+y)*(x-y)
这就是一个简单的表达式,无论这个表达式多么复杂,他最后都是计算出一个结果。
不同类型表达式的计算
例如:
#include <stdio.h> //头文件
void main() //程序入口
{
char x = 1; //把1赋给char类型的x
short y = 3; //把3赋给short类型的y
int z = x+y; //把x+y返回到int类型的z里
printf("%d\n",z); //以十进制输出结果到控制台
return; //程序结束
}
这里是两个变量x,y外加一个返回值z,它们分别是三个类型:char、short、int,那么他们是怎么运算的,我们直接去反汇编看一下就行了:
这里可以看到,程序把x的值存到了eax中,把y的值存到了ecx中,这两个寄存器都是四个字节的,也就是说,在进行相加之前先转换成了int类型,那么int类型又是四个字节,也就是先统一类型,再进行计算,这里统一类型是有先后级的。
char < short < int < float < double
这里总结一下:
- char类型跟short类型运算,最后都会转为int类型。
- 计算最终结果的类型由表达式中数据宽度最大的那个变量决定。
常见的运算符
算术运算符
+ - * / % ++ --
加减乘除、取余、自加、自减
这里我们主要说一下这个++、–,先上一段代码:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 1; //把1赋值给int类型的x里
x++; //在当前x的值的基础上加1
printf("%d\n",x); //以十进制输出结果到控制台
return; //程序结束
}
运行结果:
那么++是自加1,–自然就是减1了:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 1; //把1赋值给int类型的x里
x--; //在当前x的值的基础上减1
printf("%d\n",x); //以十进制输出结果到控制台
return; //程序结束
}
运行结果:
这个 ++与–可以放在变量前与变量后,放前放后计算出来的结果都是一样的,但是执行顺序有所不同:
++在前,先自加在进行计算。
++在后,先进行计算再自加。
改一下代码:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 1; //把1赋值给int类型的x里
x++; //x自加1
int y = x; //把x的值赋给y
printf("%d\n",y); //输出结果到控制台
return; //程序结束
}
运行结果:
反汇编看一下是怎么传值的:
先看这两行代码:
int x = 1;
x++;
这是它的汇编指令:
mov dword ptr [ebp-4],1
mov eax,dword ptr [ebp-4]
add eax,1
mov dword ptr [ebp-4],eax
先把1赋给ebp-4,再把ebp-4赋给eax,随后eax再加1,再把1赋值给ebp-4,这一波操作之后x的值就是2了。
接着是这两行代码:
int y = x;
printf("%d\n",y);
这里把2又赋值给了eax,接着把eax赋给ebp+8,接着又把ebp-8赋给了eax,然后eax压栈,输出结果。
mov eax,dword ptr [ebp-4]
mov dword ptr [ebp-8],eax
mov eax,dword ptr [ebp-8]
push eax
push offset string "%d\n" (00422fa4)
call printf (00401
090)
add esp,8
再来看看++在前会是什么样的结果:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 1; //把1赋值给int类型的x里
printf("%d\n",++x); //进行自加然后输出结果到控制台
return; //程序结束
}
反汇编:
这里可以清楚的看到,程序是先进行了自加,在计算的结果,然后输出,这就是++在前与在后的区别,–同理。
关系运算符
< <= > >= != ==
小于、小于等于、大于、大于等于、等于、不等于
关系运算符的值只能是0和1
关系运算符的值为真时,结果为1
关系运算符的值为假时,结果为0
例子:
#include <stdio.h> //头文件
void main() //程序入口
{
int a = 10; //赋值
int b = 20;
int c = a<=b; //把a<=b的结果返回给c
if(a<=b) //进行判断
{
printf("正确!\n"); //输出判断结果
}
else //否则输出另一种结果
printf("错误!\n");
return; //程序结束
}
看一下运行结果:
为什么说结果只有0和1呢?下个断点看一下C里的值就知道了:
如果正确就是1,跟我们程序运行后的结果一致(结果为真)。
逻辑运算符
&& || !
与、或、非
x>y && x<z 只有一个结果
x>y || x<z 只有一个结果
例子:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 10; //赋值
int y = 20;
int z = 30;
int r = x>y && x<z; //进行与运算,两数相与,同真则真
return; //程序结束
}
我们去反汇编看一下r的值是多少:
值为0,很明显这是正确的。
位运算符
<< >> ~ | ^ &
左移、右移、非、逻辑或、异或、逻辑与
例子:
x = 1
y = 2
//与运算,相对应的位都为1的时候,结果才为1
0000 0001
0000 0010 &
-----------
0000 0000
//或运算,相对应的位只要有一个为1,结果就为1
0000 0001
0000 0010 |
-----------
0000 0011
//异或运算,只有两个位不一样的时候,结果为1,反之为0
0000 0001
0000 0010 ^
-----------
0000 0011
//非运算,取反即可
0000 0001 ~
-----------
1111 1110
用程序验证一下:
#include <stdio.h> //头文件
void main() //程序入口
{
int x = 1; //赋值
int y = 2;
printf("与运算:%d\n",x&y); //输出相对应的结果
printf("或运算:%d\n",x|y);
printf("异或运算:%d\n",x^y);
printf("非运算:%d\n",~x);
return; //程序结束
}
运行结果:
赋值运算符
= 拓展赋值
例子:
#include <stdio.h> //头文件
void main() //程序入口
{
int z = 1; //把1赋值给z
z += 2; //相当于 int z = z+2;
printf("%d\n",z) //输出结果
return; //程序结束
}
运行结果:
运算符的优先级
这里的优先级跟小学数学一样,先算乘除,再算加减,有括号先算括号里的。
例子:
#include <stdio.h> //头文件
void main() //程序入口
{
int x; //声明变量
x = 2+3*4-8/2 //赋值
printf("%d\n",x); //输出结果
return; //程序结束
}
运行结果: