文章目录
前言
本篇介绍操作符相关的知识,包括操作符的种类、用法、优先级、结合性
一、运算符符的种类
C 语言内置了丰富的运算符,大致将其分为以下六类:算术运算符、赋值运算符、逻辑运算符、关系运算符、位运算符和其他运算符。
二、算数运算符
+ - * / %
1.对于%
,它的两个操作数必须为整数,其他四个都能作用于浮点数和整数,返回结果为余数,如5%2=1
2.对于/
,如果两个操作数都是整数,那么执行整数除法,如5/2=2
,如果有一个操作数为浮点数,执行浮点数除法,如5/2.0=2.5
三、位运算符
1.移位运算符 >>
与<<
<<
左移运算符
>>
右移运算符
注意:移位运算符的操作数只能是整数!!!
<<
左移规则:左边抛弃,右边补零,下面举例
//以整形在内存中的存储为32位举例,10的二进制表示为1010
int num = 10;//00000000 00000000 00000000 00001010
num <<= 1 //00000000 00000000 00000000 00010100
>>
右移规则:右边抛弃,左边有可能补零,有可能按符号位补齐
在C语言中,右移运算符>>将一个数的二进制表示向右移动指定的位数,移动时保持符号位不变,即如果该数的符号位为1,则移动后在左侧补1,如果符号位为0,则移动后在左侧补0。
因此,对于-1(其二进制表示为全1),进行右移操作后,得到的结果应为1111…111(根据所使用的机器字长而定)。因为符号位为1,所以在移位时左侧会一直补1,直到移位结束。
移动的位数大于或等于操作数的位数时,其结果是未定义的,不确定的,这是因为C语言规范并没有定义这种情况应该如何处理。实际上,这种情况可能会导致不同的结果,具体的行为取决于所使用的编译器和机器的架构。
因此,在编写代码时,应该避免移动的位数大于或等于操作数的位数,以避免出现未定义的行为。
如果1111…1111(二进制)是一个无符号整数,并进行右移操作,那么高位将始终填充0。
总结:右移操作时情况复杂,需要对具体情况分析,还有可能出现未定义行为,此时需要小心使用
2.& | ^ ~
在C语言中,&、|、^和~都是位运算符,可以用来操作二进制位。
&(按位与):将两个操作数的二进制位进行与运算,只有当两个位都为1时,结果才为1,否则为0。
例如, 1101 & 1010 = 1000。
|(按位或):将两个操作数的二进制位进行或运算,只有当两个位都为0时,结果才为0,否则为1。
例如, 1101 | 1010 = 1111。
^(按位异或):将两个操作数的二进制位进行异或运算,只有当两个位不同时,结果才为1,否则为0。
例如, 1101 ^ 1010 = 0111。
~(按位取反):将操作数的二进制位进行取反运算,即0变为1,1变为0。
例如, ~1101 = 0010。
四、赋值运算符
最简单的有=
复合运算符有+= -= /= *= %= <<= >>= &= |= ^=
int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。
五、逻辑运算符
在C语言中,逻辑运算符用于对逻辑值(true或false)进行操作。C语言中的逻辑运算符有三个:&&(逻辑与)、||(逻辑或)和!(逻辑非)。
&&
(逻辑与):当两个操作数都为真(即非零)时,结果为真;否则结果为假(即零)。例如,if(a && b)表示当a和b都为真时执行if语句。
||
(逻辑或):当两个操作数中有一个为真时,结果为真;否则结果为假。例如,if(a || b)表示当a或b有一个为真时执行if语句。
!
(逻辑非):操作数为真时,结果为假;操作数为假时,结果为真。例如,if(!a)表示当a为假时执行if语句。
需要注意的是,逻辑运算符的操作数只能是逻辑值(true或false),C语言中0表示假,非零表示真。在使用逻辑运算符时,还需要遵循C语言中的短路规则:当使用&&运算符时,如果第一个操作数为假,则不会执行第二个操作数;当使用||运算符时,如果第一个操作数为真,则不会执行第二个操作数。
这种规则可以避免不必要的运算和提高程序效率。
下面用两段代码举例解释
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
return 0;
}
//程序输出的结果是什么?
下面对结果进行分析,从左到右执行,a++返回值为0,所以截断,不再执行++b和d++,但a已经变为1,所以输出1、2、3、4
#include <stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ || ++b || d++;
printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
return 0;
}
这里同理,在++b处发生截断,所以d++不再执行
六、关系运算符
在C语言中,关系运算符用于比较两个值之间的关系,结果为逻辑值(true或false)。C语言中的关系运算符包括:==(等于)、!=(不等于)、>(大于)、>=(大于等于)、<(小于)和<=(小于等于)。
==
(等于):当两个操作数相等时,结果为真;否则结果为假。例如,if(a == b)表示当a等于b时执行if语句。
!=
(不等于):当两个操作数不相等时,结果为真;否则结果为假。例如,if(a != b)表示当a不等于b时执行if语句。
>
(大于):当左操作数大于右操作数时,结果为真;否则结果为假。例如,if(a > b)表示当a大于b时执行if语句。
>=
(大于等于):当左操作数大于等于右操作数时,结果为真;否则结果为假。例如,if(a >= b)表示当a大于等于b时执行if语句。
<
(小于):当左操作数小于右操作数时,结果为真;否则结果为假。例如,if(a < b)表示当a小于b时执行if语句。
<=
(小于等于):当左操作数小于等于右操作数时,结果为真;否则结果为假。例如,if(a <= b)表示当a小于等于b时执行if语句。
关系运算符通常用于比较数值、判断大小关系和排序等方面。需要注意的是,关系运算符的操作数可以是任意类型的,但要求两个操作数的类型相同,否则会进行类型转换。
七、其他运算符
1.单目运算符
+
(正号):对操作数不做任何改变,返回操作数的值。例如,+a表示a的值。
-
(负号):将操作数取反,返回相反数的值。例如,-a表示a的相反数。
!
(逻辑非):操作数为真时,结果为假;操作数为假时,结果为真。例如,!a表示a的逻辑非值。
&
(取地址):返回操作数的地址。例如,&a表示a的地址。
*
(解引用):返回指针所指向的值。例如,*p表示指针p所指向的值。
sizeof:用于计算数据类型或变量的字节大小,其语法为sizeof(type)或sizeof(variable)
~
是一种单目运算符,同时也是位运算符,上面讲解过,这里不再赘述
--和++
:它们都是用于递增或递减变量的操作符,称为自增运算符和自减运算符。它们都可以作为前缀或后缀使用。前缀运算符表示在表达式中使用变量之前进行递增或递减操作,而后缀运算符则表示在表达式中使用变量之后进行递增或递减操作。
(类型)
:类型转换运算符,也称为强制类型转换运算符或显式类型转换运算符。
2.下标引用[]
、函数调用()
和结构成员.
->
(1)[]
使用方式:array[index]
(2)()
使用方式:
function_name(argument1, argument2, …, argumentN)
其中,function_name是函数名,argument1、argument2等是传递给函数的参数。函数调用会执行函数内部的代码,并返回函数的返回值(如果有的话)。
#include <stdio.h>
void test1()
{
printf("123456\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //实用()作为函数调用操作符。
test2("c language programming");//实用()作为函数调用操作符。
return 0;
}
(3).
和->
#include <stdio.h>
struct Stu
{
char name[10];
int age;
char sex[5];
double score;
};
void set_age1(struct Stu stu)
{
stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
pStu->age = 18;//结构成员访问
}
int main()
{
struct Stu stu;
struct Stu* pStu = &stu;
stu.age = 20;//通过结构体变量进行结构成员访问
set_age1(stu);
pStu->age = 20;//通过指针进行结构成员访问
set_age2(pStu);
return 0;
}
八、隐式转换
1.整型提升
//实例1
char a,b,c;
b = 10;
c = 20;
a = b + c;
b和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于a中。
整形提升是按照变量的数据类型的符号位来提升的
整形提升的规则类似于>>
操作符,高位都是按照符号位来补
下面举例
//例1
int main()
{
char a = 0xb5;
short b = 0xb500;
int c = 0xb5000000;
if (a == 0xb5)
printf("a");
if (b == 0xb500)
printf("b");
if (c == 0xb5000000)
printf("c");
return 0;
}
程序输出为c,这是因为a和b都发生了整型提升,而符号位为1,所以截断之后变成负数
//例2
int main()
{
char a = 1;
printf("%u\n", sizeof(a));
printf("%u\n", sizeof(+a));
printf("%u\n", sizeof(-a));
return 0;
}
输出为1 4 4,因为a一旦参与运算则发生整型提升
2.隐式类型转换
#include <stdio.h>
int main()
{
int a = 0;
long long b = 1;
a = sizeof(1 ? a : b);
printf("%d\n", a);
return 0;
}
a的输出结果为8,因为发生了隐式类型转换,变为long long类型
再如
float f = 3.14;
int num = f;//隐式转换,会有精度丢失
num的值为3
下面给出转换的优先级,从高到低
long double
double
float
unsigned long int
long int
unsigned int
int
九、运算符优先级与结合性
需要注意的是,括号可以用来改变运算符的优先级和结合性,因此可以通过使用括号来明确计算顺序。另外,在使用运算符的过程中,还需要注意一些运算符的副作用,例如递增/递减运算符会改变变量的值,应该避免在表达式中多次使用它们。
总结
在使用运算符时需要注意它们的优先级和结合性,以及一些特殊的运算方式和副作用。正确使用运算符可以使代码更加清晰和易于理解。