运算符概述
相关名词
对于" a=b+c "
- 这个式子被称为表达式
- " = "和" + "被称为运算符
- 参与运算的b、c被称为操作数
- " + "操作数的个数为2,所以" + "的运算符分类为双目运算符
优先级
1、除法运算符 " / "
除号的两端都是整数时,结果也为整数。
int a = 7/2;// 7/2=3.5,取整数为3,不进行四舍五入
除号的两端有浮点数时,结果为浮点数(自动类型转换)
int a = 7/2.0;// 7/2=3.5 2.0为double型,因此结果也为double型,保留小数
2、取余运算符 " % "
取余运算符的两个操作数只能为整数。
下面为取余运算的应用:
2.1 实现多位数的分离
有一个不知多少位的数(比如说123),现在要将其各个位进行分离存入到一个数组中,要求低位存入低地址(比如1存入buf[2]、2存入buf[1],3存入buf[0])。
代码实现如下:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
/* 拆分一个整数,将各个位存入数组,第0位存入个位 */
/* 返回值:动态创建的数组首地址,失败返回NULL */
char* Split_Num(int num){
char* Split_Num_point = NULL;
char* point_tmp = NULL;
int digit = 1;//传入num的位数
int i=0;//循环控制变量
int num_tmp = num;
/* 1.遍历,直到个位 */
while(1){
num_tmp/=10;
if(num_tmp == 0){ //num/10=0代表已经到达了个位
break;
}
digit++;
}
printf("Debug:digit = %d\n",digit);
/* 2.开辟数组空间 */
//有多少位就开辟多少空间+1,多的1个空间是用来存'\0'
Split_Num_point = (char*)malloc(sizeof(char)*(digit+1));
point_tmp = Split_Num_point;
if(Split_Num_point == NULL){
printf("malloc err\n");
return NULL;
}
printf("Debug:malloc success\n");
/* 3.从最高位开始,获取每一位的值 */
num_tmp = num;
printf("Debug:num_tmp = %d\n",num_tmp);
point_tmp += digit;//指针偏移到数组末尾
*point_tmp = '\0';
point_tmp--;
for(i=digit-1;i>0;i--){
*point_tmp = num_tmp/pow(10,i);
num_tmp = num_tmp%((int)pow(10,i));
printf("Debug:i=%d,*point_tmp = %d\n",i,*point_tmp);
printf("Debug:i=%d,num_tmp = %d\n",i,num_tmp);
point_tmp--;
}
*point_tmp = num_tmp;
printf("Debug:i=%d,*point_tmp = %d\n",i,*point_tmp);
return Split_Num_point;
}
void print_str(char* str){
char* point = str;
while(*point != '\0'){
printf("%d",*point);
point++;
}
}
int main(){
char* p;
p = Split_Num(87123);
print_str(p);
return 0;
}
代码使用方法:
将一个int类型的数据传入函数Split_Num,最终返回一个char*的动态开辟的数组首地址。数组的最低位为传入数据的最低位,在上述代码中,传入87123,那么*p就是3。
3、自增自减运算符
"++"、" -- "可以放在变量前(前置),也可以放在变量后(后置)
前置:例如++val,代表先对val进行加1,再进行赋值
后置:例如val++,代表先进行赋值,再对val进行加1
int val_1=0;
int val_2=0;
printf("val_1++ = %d,val_1 = %d\n",val_1++,val_1);//先用再加,打印 0,最终val = 1
printf("++val_2 = %d,val_2 = %d\n",++val_2,val_2);//先加再用,打印 1,最终val = 1
4、位运算符
4.1 按位取反(~):
按位取反(~)只能用作于整型数据," ~ "的优先级很高,只比" () "低一级
对于有符号数和无符号数,取反都是一样的,最高位都会从0->1或1->0。
按位取反时需要考虑变量类型的存储内存大小,具体示例如下:
unsigned int a = 0x20; //0010 0000
a = a >> 2; //0000 1000
a = ~a; //1111 0111
printf("%x",a); //因为int为4字节,所以会再前面补0,取反后会全部变成1
//最终结果为0xfffffff7
4.2 按位异或(^):
按位异或(^)具有0与某个数异或等于它本身的性质,即:0^n=n
按位异或实现原地交换:
#include <stdio.h>
int main(){
int a=0x78;
int b=0x12;
a=a^b;
b=a^b;//b=0x78
a=a^b;//a=0x12
printf("a=%x,b=%x\n",a,b);
/* 运算过程解释 */
/*
a=原a^原b
b=a^原b
=(原a^原b)^原b
=原a^(原b^原b)
=原a^0
=原a
a=a^b
=原a^原b^b
=原a^原b^原a
=(原a^原a)^原b
=0^原b
=原b
*/
return 0;
}
4.3 移位(<< >>):
移位(<< >>)只能用作于整型数据。
对于无符号数,移位后的空位补0。
- 左移就相当于*2^n,比如0000 0001<<2,就时0x01*2^2 = 4,即:0000 0100
- 右移就相当于/2^n,比如0000 0100>>2,就时0x04/2^2 = 1,即:0000 0001
对于有符号数,右移位后符号位正数补0,负数补1(左移不补),其余操作与无符号数一致。
- 左移(负数):比如1000 0001<<1,这是个负数,所以最高位不补,结果为0000 0010
- 左移(正数):比如0000 0001<<1,这是个正数,所以最高位不补,结果为0000 0010
- 右移(负数):比如1000 0001>>1,这是个负数,所以最高位补1,结果为1100 0000
- 右移(正数):比如0000 0001>>1,这是个正数,所以最高位补0,结果为0000 0000
4.4 按位与、按位或( & | )
任意数 & 0 = 0,因此按位与(&)常常用作对某一位进行清零
任意数 | 1 = 1,因此按位或( | )常常用作对某一位进行置1
任意数 ^ 1 = 取反,因此按位异或(^)常常用作对某一位进行取反
- 清0第n位:a &= ~(1<<n)
- 置1第n位:a |= (1<<n)
- 取反第n位:a ^= (1<<n)
5、关系运算符
关系运算符就是>、<、==、!=这些。在使用关系运算符时,应该将常量放在左边,变量放在右边,以减少" == "写为" = "时查不出bug的情况。
例如应该写为 if(-1==val),而不是 if(val == -1)
6、逻辑运算符
逻辑运算符就是&&、||、! 这些。其中&&、||具有短路特性,即:表达式从左往右执行,若左边已经能决定表达式结果,则后面不再执行。
- &&短路特性:左边为0时,右边不再执行
- ||短路特性:左边为1时,右边不再执行
验证例程如下:
int a = 0, b = -1, c = 3, d = 4;
c = a++ && ++ b && d++;
printf(“a = %d\n b = %d\n c = %d\n d = %d\n”, a, b, c, d);
因为a++为先用再加,所以是a=0进行&&运算。0已经能决定&&运算,因此++b、d++都不再运行。最终结果为a=1、b=-1、c=0、d=4
编程规范:
因为短路特性的存在,在编程中应该将最可能为1的表达式放在||左边,最可能为0的表达式放在&&左边,以达到最大程度的使用短路特性,提高代码执行效率。
7、条件运算符
条件运算符的形式为:<条件>?<表达式1>:<表达式2>
- 若条件为1,值为表达式1的值
- 若条件为0,值为表达式2的值
验证例程如下:
int x,y=25;
x=70;
y= x++ > 70 ? 100 : 20 ;
printf("x=%d y=%d",x,y);
因为x++为先用再加,所以是x=70进行>70的判断,结果为假,y被赋值为20。因为x++,所以x值为71。
8、逗号运算符
逗号运算符从左向右执行各个表达式,值为最后一个表达式的值。
验证例程如下:
int x,y,z;
z=(x=y=5,x++);
printf("x=%d,y=%d,z=%d",x,y,z);
首先运行x=y=5,这是x=5、y=5;再运行x++,因为x++为先用再加,所以表达式值为5而不是6,因此z被赋值为5。最终结果为x=6、y=5、z=5。