引言:此文章对于C语言中的操作符进行总结说明,易错点分析,配合代码实例,希望读者能对操作符有更深层次的理解。当然其中还有一些二进制处理操作符,库函数,文章中不做过多说明,只对于操作符进行解读。
1.算数操作符:+ - * / %,
解读:其中+-*没有什么考点,对于/有两个考点:1.整数除法 2.小数除法
比如下面代码:可以放到编译器中运行,观察每个变量的结果。这点较为简单,尝试过之后便可以得到结论,所以不过多赘述。
float a = 3 / 5;
float b = 3.0 / 5;
float c = 3 / 5.0;
float d = 3 * 1.0 / 5;
float e = 3 / 5 * 1.0;
对于%需要知道两点:1.a % b,a,b都得是整数 2.范围是[0 ,b-1](控制范围,如生成一个范围内的随机数)这里提供一个随机数生成函数:需要引用time.h和stdlib.h,因为使用了srand和time函数,具体使用不做描述。
int Gen_rand_num(int min,int max)
{
srand(time(0));
int num;
//生成一个min-max之间的随机数:核心要领是明白,取模留下的是谁
num = rand() % (max-min+1)+min;
return num;
}
2. 移位操作符:<< 左移位 、>> 右移位。
解读:移位操作只对于整数操作。整数在内存中以补码的形式存放,左移位也是对其补码进行操作,具体操作是:补码整个向左移动,去掉首位,末尾补0。那么有什么作用呢?对这个数翻倍(符号不变)移动一位翻一倍(对于边界条件有待讨论,比如移动32位,移动31位)。右移位有两种:1.算术右移,2.逻辑右移,其中逻辑右移就是右边舍弃,左边补0。而算术右移是根据原来数的符号位进行补充,就是做到不改变原来二进制的符号位。
3.按位与/或/异或:& | ^
解读:这里的操作是对于二进制位进行操作:其遵循的原则都是将两个整数的二进制的补码进行比较,得到一个新的整数的补码:1.按位与:两个都为1,才为1,其余为0。2.按位或:有一个为1,就是1。3.按位异或:对应位置的数不相同,则为1,相同为0。其中按位异或与前两者不同:不会进位,所以数值比较相近,其具有很多性质:1.0^a=a,a^a=0 2.a^b=b^a 3.(a^b)^c=a^(b^c)
应用:1.可以实现不用创建第三个变量达到交换两个整数的目的(按位异或)。 2.编程统计一个整数的二进制中1的个数。(提示:按位与(a&1)和右移位)
4.赋值操作符:=
解读:将等号右边的值赋给左边。 = 与上述的算数运算符合操作运算符相结合,可以构成符合运算符。需要注意的是, = 两边的表达式是有要求的,左边为左值,右边为右值;左值就是变量,也就是可以修改的值,而右值就是常量,或者称为不可以修改的值,比如int x=5;5是一个常量,x是一个变量,符合=对于数值的要求,而当你改成5=x时,那么编译器就会报错:error C2106: “=”: 左操作数必须为左值,这就是告诉你左边的值为一个不可修改的值,常见的有宏定义常量,const常量,已定义的数组名(数组地址)等。
5.单目运算符:!(逻辑反操作)、-(负值)、&(取地址)、sizeof 、--、++、*(间接访问)、~(按位取反)、(类型)(强制类型转换)。
需要注意的操作符:
1.sizeof,它可以求 1.变量大小 2.某类型数据(比如int)的大小,返回值为int,单位为字节。
2.按位取反:对于一个整数的补码进行按位取反,得到一个新的整数的补码。可以和按位操作结合,达到修改一个整数补码的目的,可以直接操作补码的任意一位改成0/1。
3.前置++和后置++,先使用还是先++,会有不同的结果。
前置:先使用后++,后置先++再使用。例如如下代码:
int add(int x,int y)
{
return x + y;
}
int main()
{
int a = 0,b = 1, c = 2;
printf("a=%d,b=%d,c=%d\n", a++,++b, --c);
//结果:a=0,b=2,c=1
//分析:因为a是后置,b,c都是前置,所以a先使用再自增1,对于b先自增再使用,c同理
printf("a=%d,b=%d,c=%d\n", a, b, c);
//结果:a=1,b=2,c=1
a = 1, b = 1;
//在函数调用的时候,也是要分清楚先使用还是先自增。
int d = add(a++, ++b);
printf("d=%d",d);
//结果:d=3
return 0;
}
6.关系运算符:> < = ==
解读:其中需要注意的就是==是用来判断两个值是否相等的,这个值可能是数值,比如3==5就是用来判断3是否等于5,而"abd"=="abcde"是在比较两个字符串地址(首字符地址)是否相同,有些相等不能胡乱比较,可能达不到目的。注意:这些关系运算后结果为0/1是一个常量,1代表真,0代表假。比如如下代码:
int a = 1 > 3;
int b = 1 <= 2;
printf("a=%d,b=%d", a, b);
//结果:a=0,b=1
7.逻辑操作符:|| && 逻辑或/与
解读:用来连接常量表达式或者常量。注意:逻辑操作符号是从左向右进行计算判断,比如说对于c=a&&b++,如果a=0,那么b不会++,因为是&&连接,无论b是否为真,那么c的结果都是0了,但是对与a||b++,如果a为真(非0),那么b也将不会计算(又称短路现象)
//360面试题:
int a=0, b=1, c=2, i;
i = a++ && b++ && ++c;
//i=a++||b++||++c;
printf("a=%d b=%d c=%d d=%d\n", a, b, c, i);
//结果:a=1 b=1 c=2 d=0
/*结果分析:由于a先使用,所以对于 a++ && b++ 这个部分,无论b怎么样,该表达式的值一定为假,所以对于b不会再做计算,因为这个部分的值为0,所以相当于 0&&++c ,同样c无论怎样,该语句为假,所以c也不会计算*/
上面对于&&的语句进行了分析,对于||也是同理
8.三目操作符(条件操作符):[(表达式 1)?(表达式 2):(表达式 3)]
解读:如果表达式1为真,那么整个表达式的值为表达式2的值,否则为表达式3的值。例如下面代码:
int a = 2, b = 3;
int max = a < b ? a < b : b++;
printf("%d", max);
//结果:1
9.逗号表达式:(表达式 1,表达式 2,表达式 3,…,表达式 N)
解读:逗号表达式的意思是:从左向右依次执行 表达式 1至N,最后一个表达式的值为整个表达式的值。比如如下代码:输出结果为2
int a = 0, b = 1, c = 2;
if (a++, b--, c < 0)
{
c++;
}
printf("%d", c);
10.数组下标引用操作符:[ ]
解读:在数组中该操作符相当于*( ),例如如下代码:输出之后都可以修改arr[0]的值。通过*( )就可以理解,arr[1]可以理解为*(arr+1),也就是数组首元素地址+1,再解引用就对数组中第二个元素解引用,这时候对于arr[1]赋值就相当于对*(arr+1)进行赋值。所以下面的代码1[arr]也就容易理解了,相当于是对*(1+arr)赋值,也就是一样的效果。
int arr[5] = {1,2,3,4,5};
arr[1] = 315;
printf("%d\n",arr[0]);
1[arr] = 723;
printf("%d", arr[0]);
11.结构体访问成员操作符: . 和 ->
解读: . 的应用:常用语结构体变量(非结构体指针变量),比如如下结构体:
struct student
{
char name[20];
int card;
char flag;
};
int main()
{
struct student stu1; //stu1是结构体普通变量
stu1.card = 20230315; //对card变量进行赋值
return 0;
}
-> 的应用:常用于结构体指针变量,比如如下代码:
int main()
{
struct student stu1;
struct student* p = &stu1; //定义结构体指针变量存放stu1的地址
p->card =20230723; //通过箭头解应用访问内部成员
return 0;
}
对于结构体的指针变量,还有另一种访问方式:(*p).card=20230723;可以达到相同的效果,意思是先对p解应用,*p就相当于是stu1,然后再加.访问其内部成员;需要注意的是,这种写法需要将*p整体括号,因为.的优先级比->更高。
最后需要注意的是,这两种访问相当于替换了该结构体中的变量名,比如p->card=20230723,相当于对该结构体中 card=20230723 进行操作,不难发现,对于int ,double这些数值型变量这样访问没有任何问题,但是对于数组访问往往就会出现问题,比如下面代码:
int main()
{
struct student stu1;
stu1.name = "Lisa";
struct student* p = &stu1;
p->name = "lisa";
return 0;
}
可以达到修改name数组的目的吗?显然不行,因为这相当于name=”Lisa”,数组名代表该数组的首地址,地址怎么能赋成字符串呢?况且该地址还是固定的。
所以在vs2022中会出现如下错误: error C2106: “=”: 左操作数必须为左值;所谓左值就是可以修改的值,比如在程序中定义的变量,int a=6;意思就是将6赋值给a;详细说明参考上面所提到的赋值操作符。那么代码应该如何更改呢?第一种:加上[ ] 此时相当于name[ ],一个个进行赋值即可;第二种:char *strcpy(char *dest, const char *src);利用strcpy函数
注:在传递函数参数的时候,往往传递指针,因为更加的节省空间。
12.函数调用操作符:()
解读:对于该符号来讲,了解即可,可以判断是否是函数,比如abs():整数绝对值函数,又如memcpy(),你可以判断知道这是一个函数,在不知道时候可以查阅资料。