C 语言 运算符 全网最全整理
码字不易,对你有帮助 点赞/转发/关注 支持一下作者
微信搜公众号: 不会编程的程序圆 看更多干货,获取第一时间更新
想看更好的排版,可以看原文
网页链接mp.weixin.qq.com思维导图
![2b524a495c65ff93738e0e1ca34edc36.png](https://img-blog.csdnimg.cn/img_convert/2b524a495c65ff93738e0e1ca34edc36.png)
目录
@[toc]
正文
一 算数操作符
+
-
*
/
%
: % 左右两边的数必须都为整数
二 移位操作符
>>
: 右移<<
: 左移
例1:b = 20
![bfab2b094a8b7e987f9c194bd95df7cc.png](https://img-blog.csdnimg.cn/img_convert/bfab2b094a8b7e987f9c194bd95df7cc.png)
例2:b = -4
![21bd01dee1b001e162e685149a1a1bc0.png](https://img-blog.csdnimg.cn/img_convert/21bd01dee1b001e162e685149a1a1bc0.png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APrIn3AF-1585038906075)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
注意1:
左移直接在空的地方补0 如例1
右移有两种情况:
1.逻辑位移 补0
2.算数位移 补1 如例2(一般都是这种情况)
注意2:
移位操作符,不要移动负数位,这是标准未定义。
如:num >> -1//error
三 位操作符
&
(按位与)|
(按位或)^
(按位异或)
例3:按位与
![ba85d3e7c910eb13f083b4a00929061d.png](https://img-blog.csdnimg.cn/img_convert/ba85d3e7c910eb13f083b4a00929061d.png)
例4:按位或
![3949b448cd79551867f5249008c2fb4b.png](https://img-blog.csdnimg.cn/img_convert/3949b448cd79551867f5249008c2fb4b.png)
例5:按位异或
![9bdb92279f68d948fd5788ce1787b7ac.png](https://img-blog.csdnimg.cn/img_convert/9bdb92279f68d948fd5788ce1787b7ac.png)
例6:异或的应用——交换两个值的内容
方法1:
int a,b,c;
c = a;
a = b;
b = c;
方法2:(如果a,b很大可能会溢出)
int a,b;
a = a + b;
b = a - b;
a = a - b;
方法3(异或法):
int a, b;
a = a^b;
b = a^b;
a = a^b;
例7:怎么求一个二进制位中1的个数
#include<stdio.h>
int CountOneBit(unsigned int n){//解决负数无法计算问题,这种运算运算的是补码
int count = 0;
while(n){//类比十进制中每位数的求法
if(n%2 == 1)
count++;
n/=2;
}
return count;
}
int main(){
int n = 0;
int num_1 = 0;
printf("请输入一个数:");
scanf("%d",&n);
num_1 = CountOneBit(n);
printf("%d的二进制序列中有%d个 1",n,num_1);
}
unsigned的作用:
unsigned就是将这个二进制数最高位的符号位变成计数位。下面我们举个例子帮大家理解一下
如果我们输入的是-1
-1%2 == -1
-1/2 = 0
这样输出的count为0
但是我们知道-1的补码是11111111111111111111111111111111
这样我们的代码就局限在正整数
如果加上unsigned 虽然我们输入的是-1 但是程序计算是是按照 unsigned int 的最大值,这样就避免了这个问题
更多位运算相关示例: https://github.com/hairrrrr/linux.ccode/tree/master/Bit/ClassCode/2020-1-7%EF%BC%888%EF%BC%89
四 赋值操作符
=
a = b = c
它的意义是将c的值赋给b,再将b的值赋给a。其实这样理解不够准确,其实应该这么写:
a = (b = c)
先将c的值赋给b 然后将这个整体,即b的值赋给a
五 复合赋值符
+= -= *= = %= >>= <<= &= |= ^=
六 单目操作符
!
-
=
&
(取地址)sizeof
(操作数的类型长度)~
(对一个数的二进制位按位取反)--
++
(前置,后置)*
(解引用)(类型)
(强制类型转换)
例8:!的应用
应用!与flag来判断情况做出选择
if(flag){
flag为真进入循环;
}
if(!flag){
flag为假进入循环;
}
printf函数打印格式
%#p 0XCCCCCC
%p 00CCCCCCC
%x cccccc
%X CCCCCC
1
int a = 0;
sizeof(a)=4
sizeof(int) = 4
sizeof a √
sizeof int ×
2
sizeof求数组大小 sizeof(arr)
sizeof求数组元素个数 sizeof(arr)/sizeof(arr[0])
切记!!!sizeof不能再函数内部求指针数组的大小
3
int a = 10;
short s =3;
printf('%dn",sizeof(s = a+4));
printf("%dn",a);
printf("%dn",s);
输出: 2 10 3
赋值并不会将类型一同赋予左值
sizeof在编译阶段就已经运行结束了(sizeof 被 换成 2)
而s = a + 4要到生成可执行文件之后才会完成
编译(.c) -- 链接(.exe)
4
小心细节问题。进入函数后 ch实际上变成了指针变量。
![a74daca10b86588ec1fe8b53db969f1f.png](https://img-blog.csdnimg.cn/img_convert/a74daca10b86588ec1fe8b53db969f1f.png)
例9:~ 的应用
若想完成一下操作:
15 00001111
将从右数第4位变成0 ,应该如何操作?
a = (1<<4-1) 00001000
b = ~(a) 11110111
b&15 00000111
所以可以这么写:
a &= (~(1<<4-1));
如何变回来呢?
00001000 1<<4-1
00000111 | (1<<4-1) 即可
七 关系操作符
>
>=
<=
!=
==
注意:不要将 ‘ == ’ 写成 ‘ = ’
八 逻辑操作符
&&
||
例11 &与&&的差异:
![e6ed386db783112283c80449cc8a1936.png](https://img-blog.csdnimg.cn/img_convert/e6ed386db783112283c80449cc8a1936.png)
例12 这个题值得思考
![f08ff022b1a10ee7369155a265acf347.png](https://img-blog.csdnimg.cn/img_convert/f08ff022b1a10ee7369155a265acf347.png)
在&&的判断中如果一边位假(0)那么程序就会停止向后面判断。所以++b与d++并没有执行
在 || 的判断中如果一边为真,则停止继续向下判断。(如:++a || b++ || c)
补充:
![97106a551f11f49643e5a731f2c8eff5.png](https://img-blog.csdnimg.cn/img_convert/97106a551f11f49643e5a731f2c8eff5.png)
![2dc3352dcf9b89492048d389ec284b75.png](https://img-blog.csdnimg.cn/img_convert/2dc3352dcf9b89492048d389ec284b75.png)
![86885002b2b321e9beee1ba235f03005.png](https://img-blog.csdnimg.cn/img_convert/86885002b2b321e9beee1ba235f03005.png)
九 条件操作符
a > b ? a : b
十 逗号表达式
a,b,c,d,.....n
逗号表达式整体的值等于最后一个表达式的值
例13 思考题
int main() {
int a = 0;
int b = 1, c = 2;
a = (a = b, b += c, c--);
printf("case1:%dn", a);
a = 0,b = 1, c = 2;
a = (a < 0, a++, b = a);
printf("case2:%dn", a);
a = 0, b = 1, c = 2;
a = (a<0, b<a);
printf("case3:%dn", a);
a = 0, b = 1, c = 2;
if (a = b + 1, c = a / 2 - 1, c == 0)
printf("case4:%dn", c);
}
![d21d795b88e7d927cb7ab72b495ac762.png](https://img-blog.csdnimg.cn/img_convert/d21d795b88e7d927cb7ab72b495ac762.png)
另一种代码书写方式:
//一般情况
a = get_val();
count_val(a);
while (a > 0) {
//业务处理
a = get_val();
count_val(a);
}
//应用逗号表达式
while (a = get_val(), count_val(a), a > 0) {
//业务处理
}
合理应用逗号表达式可以简化代码。
11 其他
1. [] 下标引用操作符
对于数组 arr[5] = {1,2,3,4,5}
我们一般的用法是:
arr[0],arr[1],arr[2]…..其中arr[0]就代表访问数组中第一个元素,arr[1]代表访问第二个以此类推
学习了指针之后我们知道:
arr代表数组首元素的地址。
arr[0],arr[1],arr[2]…我们可以改写成:arr,(arr+1),(arr+2)….. (arr可以理解为*(arr+0))
进一步思考:
(arr+1)可以改写成(1+arr)
那么arr[1]可否写成1[arr]呢?答案是肯定的。
所以我们就有了一下结论:
1[arr] == arr[1] == *(arr+1) == *(1+arr)
事实上,无论哪一种写法,程序再最终编译的时候都会转化为:*( arr+1)
2. ( ) 函数调用操作符
例14.有参数调用
int add(int x, int y){
return x+y;
}
int main(){
int a = 1;
int b = 2;
printf("%d",add(a,b));
}
例15.无参数调用
void test(){
printf("看到这里的都是人才!!!n");
}
int main(){
test();
}
3.访问一个结构的成员
.
(结构体.成员名)->
(结构体指针 -> 成员名)
例16.
struct Stu{
char name[20];
int age;
}stu;
int main(){
stu = {"张三",20};
struct Stu *ps = &stu;
//下面三种写法都是正确的
printf("%s %dn",stu.name,stu.age);
printf("%s %dn",(*ps).name,(*ps).age);
printf("%s %dn",ps->name,ps->age);
}
12 表达式求值
1. 隐式类型转换
- C的整型算术运算总是至少以缺省整型类型的精度来进行的。
- 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
整型提升方法: 正数的整型高位补充0 负数补充1
例17.整型提升示例
注:char是有符号的
![ad21450c13203b7d67d666e8bd25c4a1.png](https://img-blog.csdnimg.cn/img_convert/ad21450c13203b7d67d666e8bd25c4a1.png)
补充:字符类型的反码(1字节)及其表示的值
![dcee99156fb8529c0c4bbb9ed22f8fe4.png](https://img-blog.csdnimg.cn/img_convert/dcee99156fb8529c0c4bbb9ed22f8fe4.png)
总结: char 的值范围是: -128 ~ 127 unsigned char 值的范围是: 0 ~ 255
例18.整型提升在程序中的证明
int main()
{
char a = 0x41;
char b = 0xFF;//0xFF -> 255 11111111
int c = 0xb6000000;
if (a == 0x41) {//正数原码补码一致,整型提升补码不变
printf("an");
printf("%d %cn", a, a);
}
if (b == 0xFF) {
printf("bn");
}
//在判断 b==0xFF 时 要对b进行整型提升
//b 11111111 -> 11111111 11111111 11111111 11111111 而 0xEF的补码依然是11111111 所以这两个补码时不同的
printf("%d %cn", b, b);
//输出时 依然要对b进行整型提升
// 11111111 -> 补码:11111111 11111111 11111111 11111111
//反码:11111111 11111111 11111111 11111110
//原码:10000000 00000000 00000000 00000001 即:-1
if(c==0xb6000000)
printf("cn");
return 0;
}
例子20.整型提升的再一次证明
![3cdf10da19d630182a11ff2f7638d113.png](https://img-blog.csdnimg.cn/img_convert/3cdf10da19d630182a11ff2f7638d113.png)
2. 算数转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double 8byte double 8byte float 4byte unsigned long int 4byte long int 4byte unsigned int 4byte int 4byte
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
附:各类型变量在内存中占的字节(32位)
int main() {
printf("%dn", sizeof(char));//1
printf("%dn", sizeof(int));//4
printf("%dn", sizeof(unsigned int));//4
printf("%dn", sizeof(long));//4
printf("%dn", sizeof(unsigned long));//4
printf("%dn", sizeof(long long int));//8
printf("%dn", sizeof(float));//4
printf("%dn", sizeof(double));//8
printf("%dn", sizeof(long double));//8
}
3. 操作符属性
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
在 Github 上看更全的文章目录: https:// github.com/hairrrrr/C-C rashCourse
这个系列文章的代码都会上传上去,欢迎 star
以上就是本次的内容。
如果文章有错误欢迎指正和补充,感谢!
最后,如果你还有什么问题或者想知道到的,可以在评论区告诉我呦,我可以在后面的文章加上你们的真知灼见。
关注我,看更多干货!
我是程序圆,我们下次再见。