万字细文带你吃透操作符(C语言深入解剖篇)
作者介绍:友友们好我是沐曦希,可以叫我小沐💕
作者主页:沐曦希的个人博客主页.🎉
作者的gitee:https://gitee.com/muxi-c-language.
零基础学习C语言系列:
🎈 https://blog.csdn.net/m0_68931081/category_11742786.html.
🎉小沐和友友们一样喜欢编辑,天天敲代码🤭,沉迷学习,日渐消瘦。很荣幸能向大家分享我的所学,和大家一起进步,成为合格的卷王。✨如果文章有错误,欢迎在评论区✏️指正。那么开始今天的学习吧!😘
文章目录
-
- 🎈操作符
- 🎈算术操作符
- 🎈移位操作符
-
-
- ✨左移操作符
- ✨右移操作符
-
-
- 🎉逻辑移位
- 🎉算术移位
-
-
- 🎈位操作符
-
-
- 🎉&--按位与
- 🎉|--按位或
- 🎉^--按位异或
-
- 🎈赋值操作符
-
-
- ✨复合赋值操作符
-
- 🎈单目操作符
-
-
- ✨单目操作符介绍
- ✨sizeof 和 数组
-
- 🎈关系操作符
- 🎈逻辑操作符
- 🎈条件操作符
- 🎈逗号表达式
- 🎈下标引用、函数调用和结构成员
-
-
- ✨[ ] 下标引用操作符
- ✨( ) 函数调用操作符
- ✨访问一个结构的成员
-
- 🎉写在最后
🎈操作符
指令系统的每一条指令都有一个操作符,它表示该指令应进行什么性质的操作。不同的指令用操作符这个字段的不同编码来表示,每一种编码代表一种指令。
其中操作符分为:
- 算术操作符
2.移位操作符
3.位操作符
4.赋值操作符
5.单目操作符
6.关系操作符
7.逻辑操作符
8.条件操作符
9.逗号表达式
10.下标引用、函数调用和结构成员
🎈算术操作符
+ - *(乘) /(除) %(取模)
注意:1.✏️除了% 操作符之外,其他的几个操作符可以作用于整数和浮点数。
#includeint main(){int a = 12;int x = 5;float b = 7.0;float c = -6.0;float d = b + c;float e = b - c;float f = b * c;float g = b / c;int ret = a % x;printf("d=%f\ne=%f\nf=%f\ng=%f\nret=%d", d, e, f, g, ret);return 0;}
2.✏️对于/ 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
#includeint main(){int a = 20;int b = 10;int c = 20.0;int d = 10.0;int x = a / b;float y = c / d;printf("x=%d\ny=%f", x, y);return 0;}
3.✏️% 操作符的两个操作数必须为整数。返回的是整除之后的余数。
#includeint main(){int a = 8;int b = 3;int c = a % b;printf("%d", c);return 0;}
🎈移位操作符
移位操作符针对的是二进制,所以移动的是二进制,每次运算时,第一步需要转换成对应的二进制,再进行移位。
<< 左移操作符>> 右移操作符注:移位操作符的操作数只能是整数。
✨左移操作符
移位规则:
左边抛弃、右边补0
#includeint main(){int num = 2;printf("num<<1=%d", num << 1);return 0;}
当num=-2时:
#includeint main(){int num = -2;printf("num<<1=%d", num << 1);return 0;}
左移一位相当于该数字乘于2,
✨右移操作符
右移操作符相对复杂一点。分为逻辑移位和算术移位。在转换成二进制时,二进制的第一个数字表示正负,即当第一个数字为0时,该数字为正,为1时,该数字为负
例如:
✏️小知识:整数的二进制表示有三种方式:原码,反码,补码。
正整数的原码,反码和补码相同。
负整数的原码,反码和补码不相同。
其中在存储时候,存储的是补码。
🎉逻辑移位
移位规则:
左边用0填充,右边丢弃
#includeint main(){int num = -2;int a = num >> 1;printf("%d", a);return 0;}
🎉算术移位
移位规则:
左边用原该值的符号位填充,右边丢弃
#includeint main(){int num = -2;int a = num >> 1;printf("%d", a);return 0;}
小沐用的编译器支持的是算术右移。
警告⚠ :
对于移位运算符,不要移动负数位,这个是标准未定义的。(浮点数也不行)
例如:
int num = 10;num>>-1;//error
右移一位相当于除与2。
🎈位操作符
位操作符是二进制的运算,故与移位操作符一样,运算的第一步应讲操作数转换成二进制。位操作符只适用于整型。
位操作符有:
& //按位与| //按位或^ //按位异或
注:他们的操作数必须是整数。
🎉&–按位与
运算规则:有零则为零,同为一才为一。✏️
0&0=0 1&0=0 1&1=0 0&1=0
例如:
#includeint main(){int a = 3;int b = -5;int c = a & b;printf("%d", c);return 0;}
🎉|–按位或
运算规则:有1则为1,同为零才为零。✏️
1|1=1 1|0=1 0|0=1 0|1=1
例如:
#includeint main(){int a = 3;int b = -5;int c = a | b;printf("%d", c);return 0;}
🎉^–按位异或
运算规则:相同为0,不相同为1。✏️
1^0=1 0^1=1 0^0=0 1^1=0
例如:
#includeint main(){int a = 3;int b = -5;int c = a ^ b;printf("%d", c);return 0;}
练习:
不能创建临时变量(第三个变量),实现两个数的交换。
//代码一#includeint main(){int a = 3;int b = 5;printf("交换前:a=%d,b=%d\n", a, b);a = a + b;b = a - b;a = a - b;printf("交换后:a=%d,b=%d\n",a, b);return 0;}
代码一容易溢出,当a和b都很大时,容易导致a=a+b溢出。
故:
//代码二#include 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;}
通过:
3^3=0 0^5=5 a^a=0 0^a=a即3^3^5=0^5=5 3^5^3=5
则按位异或支持交换律。
🎈赋值操作符
在写代码时候可以通过赋值操作符给变量赋值,从而得到想要的结果。
int weight=120//初始化weight=90//赋值//注:初始化和赋值不一样int high=175//初始化high=185///赋值//赋值操作符可以连续使用,比如:int a = 10;int x = 0;int y = 20;a = x = y+1;//连续赋值//先x=y+1=20+1=21,后a=x=21//这样的代码感觉怎么样?//那同样的语义,你看看:x = y+1;a = x;//这样的写法是不是更加清晰爽朗而且易于调试。
✨复合赋值操作符
复合赋值操作符的运算规则和前面的类似。
+= -=*= /= %=>>= <<=&= |= ^=
这些运算符都可以写成复合的效果。
int a+=5//a=a+5int a-=5//a=a-5int a*=5//a=a*5int a/=5//a=a/5int a%=5//a=a%5int a>>=5//a=a>>5int a<<=5//a=a<<5int a&=5//a=a&5int a|=5//a=a|5int a^=5//a=a^5
🎈单目操作符
单目操作符只有一个操作数。
✨单目操作符介绍
! 逻辑反操作- 负值+ 正值& 取地址sizeof 操作数的类型长度(以字节为单位)~ 对一个数的二进制按位取反-- 前置、后置--++ 前置、后置++* 间接访问操作符(解引用操作符)(类型) 强制类型转换
//! 逻辑反操作#includeint main(){int flag = 1;if (!flag)//flag为假,进入if语句{printf("是沐曦希呀\n");}if (flag)//flag为真,进入if语句{printf("是小沐呀\n");}return 0;}
// -(负值) +(正值)//负负得正 正正得正 负正得负#includeint main(){int a = -10;int b = +a;int c = -a;printf("b=%d\nc=%d\n", b, c);return 0;}
//&--取地址操作符#includeint main(){int a = 0;printf("%p\n", &a);//打印a的地址return 0;}
//sizeof 操作数的类型长度(以字节为单位)#includeint main(){int a = 10;int n = sizeof(a);//计算的是a所占的内存的大小,单位是字节printf("n=%d\n", n);//szieof是一个单目操作符//计算的是变量所占内存空间的大小int set = sizeof(int);//计算类型所创建的变量所占据空间的大小,单位是字节printf("set=%d\n", set);int sz = sizeof a;//操作符可以省略变量的(),但是类型不能省去printf("sz=%d\n", sz);return 0;}
//~ 对一个数的二进制按位取反//即将0变成1,1变成0#includeint main(){int a = 0;printf("%d\n", ~a);return 0;}
#includeint main(){int a = 3;printf("%d\n", ~a);return 0;}
//++ 前置、后置++#includeint main(){int a = 3;int b = ++a;//前置++,先++,后使用//a=a+1,b=aint c = a++;//后置++,先使用,后++//c=a,a=a+1printf("a=%d,b=%d,c=%d", a, b, c);return 0;}
//-- 前置、后置--#includeint main(){int a = 3;int b = --a;//前置--,先--,后使用//a=a-1,b=aint c = a--;//后置--,先使用,后--//c=a,a=a-1printf("a=%d,b=%d,c=%d", a, b, c);return 0;}
#includeint main(){int a = 10;printf("%d\n", a--);//后置--printf("%d\n", a);int i = 0;for (i = 0; i < 3; i++){printf("%d ", i);}printf("\n");for (i = 0; i < 3; ++i){printf("%d ", i);}//在for语句中前置和后置无区别,只是前置效率高。return 0;}
//* 间接访问操作符(解引用操作符)//通常与指针一起使用#includeint main(){int a = 10;int* p = &a;*p = 20;//a=20printf("a=%d\n", a);printf("*p=%d\n", *p);printf("p=%p\n", p);return 0;}
//(类型) 强制类型转换//即强制将一种类型转换成另一种类型#includeint main(){int a = (int)3.14;printf("%d\n", a);return 0;}
✨sizeof 和 数组
初学C语言时,经常用到sizeof来求数组的大小和元素个数等。
需要区别strlen和sizeof。
sizeof是一个单目操作符,不是一个函数。
strlen是一个库函数,头文件为string.h,是用来求字符串的长度。
例如:
#include#includevoid test1(int arr[]){printf("arr[]=%d\n", sizeof(arr));}void test2(char ch[]){printf("ch[]=%d\n", sizeof(ch));}int main(){int arr[10] = { 0 };char ch[10] = { 0 };printf("arr[10]=%d\n", sizeof(arr));printf("ch[10]=%d\n", sizeof(ch));test1(arr);test2(ch);return 0;}
注意:1.指针变量不管所指的变量是什么类型,其大小都是4或者8个字节。✏️
当配置是x86时,大小为4个字节;配置为x64时,大小为8个字节。
2.数组作为参数传参时候,传的是数组的首元素的地址。
小知识:
//strcmp是用来比较字符串的内容//strcpy是用来拷贝内容的#include#includeint main(){char arr[10] = { 0 };char ch[5] = "abcd";strcpy(arr, "母亲节快乐!");printf("%d\n", sizeof(ch));printf("%d\n", strlen(ch));if (~strcmp(ch, "abc")){printf("祝小沐的妈妈");}printf("%s\n", arr);return 0;}
strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1str2,则返回正数。
strlen是用来求字符串的长度。
strlen遇到’\0’则停止并返回,只会计算‘\0’之前的元素个数。
sizeof可以求变量(类型)所占空间的大小。
🎈关系操作符
>>=<<=!= 用于测试“不相等”== 用于测试“相等”
这些关系运算符比较简单,但是我们要注意一些运算符使用时候的陷阱。
例如:在编程的过程中把==写成=,导致错误
#includeint main(){int a = 0;if (a = 1){printf("hehe\n");}if (a == 2){printf("haha\n");}return 0;}
if("abc"=="abcdef")//这样写是比较两个字符串的首字符的地址//比较字符串用strcmp
🎈逻辑操作符
逻辑操作符只关注真假,真则返回1,假则返回0。
&& 逻辑与|| 逻辑或
区分 逻辑与(&&) 和 按位与(&)
区分 逻辑或(||) 和 按位或(|)
#includeint main(){printf("%d\n", 1 & 2);printf("%d\n", 1 && 2);printf("%d\n", 1 | 2);printf("%d\n", 1 || 2);return 0;}
//练习1#include int main(){int i = 0, a = 0, b = 2, c = 3, d = 4;i = a++ && ++b && d++;//后置++,因为a=0,即为假,故后面的++b和d++不会运行,只运行a=a+1printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0;}
//练习2#include int main(){int i = 0, a = 0, b = 2, c = 3, d = 4;i = a++||++b||d++;//因为前置++b,b=3,故为真,即运行a++,++b和d++printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);return 0;}
&&–左边为假,即为假,则右边就不用计算了。
||–左边为真,即为真,则右边就不用计算了。
🎈条件操作符
条件操作符也叫三目操作符
exp1 ? exp2 : exp3
//使用条件表达式实现找两个数中较大值。#includeint main(){int a = 10;int b = 20;printf("%d\n", a > b ? a : b);return 0;}
if (a > 5)b = 3;elseb = -3;//转换成条件表达式#includeint main(){int b = 0;int a = 0;b = (a > 5 ? 3 : -3);return 0;}
🎈逗号表达式
exp1, exp2,exp3...expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
//代码1int a = 1;int b = 2;int c = (a>b, a=b+10, a, b=a+1);//逗号表达式//c是多13//代码2if (a =b + 1, c=a / 2, d > 0)//if(d>0)//代码3a = get_val();count_val(a);while (a > 0){//业务处理 a = get_val(); count_val(a);}//如果使用逗号表达式,改写:while (a = get_val(), count_val(a), a>0){ //业务处理}
🎈下标引用、函数调用和结构成员
✨[ ] 下标引用操作符
操作数:一个数组名 + 一个索引值
#includeint main(){int arr[10] = { 0 };arr[7] = 8;//[]是下标引用操作符(双目操作符:arr和7是操作数)7[arr] = 9;//arr[7]-->*(arr+7)-->*(7+arr)-->7[arr]//arr是数组的首元素地址//arr+7就是跳过7个元素,指向第8个元素。//*(arr+7)就是第8个元素printf("%d\n", arr[7]);printf("%d\n", 7[arr]);return 0;}
✨( ) 函数调用操作符
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include void test1(){printf("hehe\n");}void test2(const char* str){printf("%s\n", str);}int main(){test1(); //实用()作为函数调用操作符。//操作数只有一个,即为test1test2("hello");//实用()作为函数调用操作符。//操作数有两个,test2和"hello"。return 0;}
int c=Add(a,b);//()是函数调用操作符,操作数:Add,a,b//函数调用操作符()的操作数至少有一个,即为函数名
✨访问一个结构的成员
. 结构体.成员名-> 结构体指针->成员名
#include 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;}
#include #includestruct Stu{char name[20];int age;double score;};void set_stu(struct Stu* ps){//strcpy((*ps).name, "zhangsan");//(*ps).age = 20;//(*ps).score = 100.0;strcpy(ps->name, "zhangsan");ps->age = 20;ps->score = 100.0;}void print_stu(struct Stu* ps){printf("%s %d %lf\n", ps->name, ps->age, ps->score);}int main(){struct Stu s = { 0 };set_stu(&s);print_stu(&s);return 0;}
🎉写在最后
那么今天的学习就到这里了。友友们觉得不错的可以给个关注,点赞或者收藏哦!😘感谢各位友友们的支持。以下的代码希望各位大佬们自行检验哦,毕竟亲手操作让记忆更加深刻。
你的❤️点赞是我创作的动力的源泉
你的✨收藏是我奋斗的方向
你的🙌关注是对我最大的支持
你的✏️评论是我前进的明灯
创作不易,希望大佬你支持一下小沐吧😘
下一期见了!