文章目录
- 前言
- 算术操作符
- 示例
- 移位操作符
- 原码,反码 与补码
- 正数
- 负数
- 计算
- 左移<<
- 右移>>
- 位操作符
- 例题
- 赋值操作符
- 单目操作符
- `sizeof` 操作符
- 关系操作符
- 逻辑操作符
- 短路现象
- 条件操作符
- 逗号表达式
- 下标引用、函数调用和结构成员
- 表达式求值
- 算术转换
- 操作符属性
欢迎讨论:
如果你发现文章中有任何错误或不完善的地方,欢迎在评论区提出建议和指正。如果对某些内容有疑问,也非常期待你在评论区与我互动。
点赞+关注:
如果这篇文章对你有所帮助,不妨点赞并关注我哦~ 你的支持是我继续创作的动力!
前言
本篇文章将深入探讨 C 语言中的各种操作符,包括算术操作符、移位操作符、位操作符等,帮助大家更好地理解和使用这些操作符。
算术操作符
C语言中的基本算术操作符包括:+
、-
、*
、/
和 %
。
- 加法 (
+
)、减法 (-
)、乘法 (*
)、除法 (/
)、取余 (%
) 都可以作用于整数和浮点数。 - 除法 (
/
):如果两个操作数都是整数,则执行整数除法;如果有一个操作数为浮点数,则执行浮点除法。 - 取余 (
%
):仅适用于整数,返回整除后的余数。
示例
int a = 5, b = 2;
printf("a / b = %d\n", a / b); // 输出: 2
printf("a %% b = %d\n", a % b); // 输出: 1
-
除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
-
对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
-
% 操作符的两个操作数必须为整数。返回的是整除之后的余数。
移位操作符
C语言提供了两种移位操作符:左移 (<<
) 和右移 (>>
)。
- 左移 (
<<
):将操作数的位向左移动,右边用零填充。 - 右移 (
>>
):将操作数的位向右移动,左边填充根据操作数的符号位(算术移位)或零(逻辑移位)。
注意:移位操作符只适用于整数类型
相信大家还有点懵,无妨,我们来看看原理。
原码,反码 与补码
原码、反码和补码是2进制的表示形式
正数
正数的原码、反码、补码是相同的
int a = 10;
// //00000000000000000000000000001010 - 原码// //00000000000000000000000000001010 - 反码// //00000000000000000000000000001010 - 补码
负数
负数的原码、反码、补码要经过计算的
计算
原码->反码:符号位不变,其余取反
反码->补码:+1
补码->原码:先取反(符号位不变)再+1(或先-1再取反(符号位不变))
注意:符号位为第32位
int a = -10;
// //10000000000000000000000000001010 - 原码// //11111111111111111111111111110101 - 反码// //11111111111111111111111111110110 - 补码//// //11111111111111111111111111110110 - 补码// //10000000000000000000000000001001// //10000000000000000000000000001010 - 原码
左移<<
内存中存储的起始是:补码的二进制
所以在参与移位的时候,移动后都是补码
移位规则:
左边抛弃、右边补0
注意:不管怎么移,符号位的值永远不变。
右移>>
移位规则:
右移运算分两种:
- 逻辑移位
左边用0填充,右边丢弃- 算术移位
左边用原该值的符号位填充,右边丢弃
取决于编译器,算术移位是更常见的,VS,DEV,VC++,gcc等等。
位操作符
&//按位与:有0则为0,全1才为1
|//按位或:有1则为1,全0才为0
^//按位异或:相同则为1,相异则为0
注意:操作数必须为整数
异或^符合结合律
例题
不能创建临时变量,实现两数交换
#include <stdio.h>int main()
{
int a = 10;
int b = 20;
a = a^b;
b = a^b;
a = a^b;
printf("a = %d b = %d\n", a, b);
return 0;
}
这个代码看似简洁,实则并不好。
1.可读性差,对读者并不友好。
2.时间复杂度和空间复杂度都不如创建临时变量更优。
平时实现需要交换两个数时,推荐使用临时变量。
另外还有一种解法:
#include<stdio.h>int main()
{
int a = 10;
int b = 20;
a = a + b;
b = a - b;
a = a - b;
printf("a = %d b = %d\n", a, b);
return 0;
}
赋值操作符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
这些运算符都可以写成复合的效果。
比如:
int x = 10;
x = x+10;
x += 10;//复合赋值
这样写更加简洁
单目操作符
! 逻辑反操作- 负值+ 正值& 取地址sizeof 操作数的类型长度(以字节为单位)~ 对一个数的二进制按位取反-- 前置、后置--++ 前置、后置++* 间接访问操作符(解引用操作符)(type) 强制类型转换
sizeof
操作符
关于sizeof
,可以求变量(类型)所占空间的大小,其返回值类型为size_t
,用%zd
int a = 10;
printf("Size of a: %zd bytes\n", sizeof(a));
关系操作符
>
>=
<
<=
!=
用于测试“不相等”
==
用于测试“相等”
注意:==
用于测试相等,=
为赋值操作符,二者不可混淆。
逻辑操作符
逻辑操作符用于逻辑判断:
&& 逻辑与|| 逻辑或
短路现象
来看一道题:
360笔试题
程序输出的结果是什么?
#include <stdio.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\nd = %d\n", a, b, c, d);
return 0;
}
为什么呢?
- 在逻辑与中,左边的表达式为假时,程序就不再继续判断右边表达式了,直接进入下一条语句。
- 同样的,在逻辑或中,左边的表达式为真时,程序就不再继续判断右边表达式了,直接进入下一条语句。
即
- 逻辑与 (
&&
):如果左边表达式为假,则右边表达式不再计算。 - 逻辑或 (
||
):如果左边表达式为真,则右边表达式不再计算。
这就是短路。
条件操作符
条件操作符(?:
)是一个三目运算符,格式为:
exp1 ? exp2 : exp3
当 exp1
为真时,结果为 exp2
,否则为 exp3
。
举个例子
if (a > 5)
b = 3;
else
b = -3;转换成条件表达式,是什么样?a > 5 ? b = 3 : b = -3;
逗号表达式
exp1,exp2,exp3,…expN
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
但最后一个表达式的结果受前面的表达式影响。
int a = (1, 2, 3); // a 赋值为 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;
}
表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
算术转换
C语言会根据操作数的类型自动进行类型转换。类型转换的顺序从高到低为:
long double
double
float
unsigned long int
long int
unsigned int
int
在运算过程中,如果需要,C语言会将较低类型的操作数转换为较高类型后进行运算。
操作符属性
复杂表达式的求值有三个影响的因素。
-
操作符的优先级
-
操作符的结合性
-
是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
常用操作符优先级图表: