前言
💖作者:龟龟不断向前
✨简介:宁愿做一只不停跑的慢乌龟,也不想当一只三分钟热度的兔子。
👻专栏:C++初阶知识点👻工具分享:
如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持博主🙊,如有不足还请指点,博主及时改正
C语言操作符
1.操作符分类
2.算术操作符
+ - * / %
1. 除了 %
操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 /
操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
#include<stdio.h>
int main()
{
printf("%d\n", 3 / 2);//整数除法
printf("%.1f\n", 3.0 / 2);//浮点数除法
}
3. %
操作符的两个操作数必须为整数。返回的是整除之后的余数。
1.1%的应用 – 求解最大公因数
方法:辗转相除法
#include<stdio.h>
int max_common_fac(int m, int n)
{
int r = 0;
do
{
r = m % n;
m = n;//m取上一次的除数
n = r;//n取上一次的余数
} while (r);//直到r为0,此时的除数为最大公因数
return m;
}
int main()
{
int m = 0, n = 0;
scanf("%d %d", &m, &n);
int ret = max_common_fac(m, n);
printf("%d和%d的最大公因数为%d\n", m, n ,ret);
return 0;
}
1.2%的应用 – 拆出一个数的每一位
#include<stdio.h>
//为了让效果明显一点,咱们将每一位打印出来
void seq_print(int n)
{
if (n > 9)
{
seq_print(n / 10);
}
printf("%d ", n % 10);
return;
}
int main()
{
int n = 0;
scanf("%d", &n);
seq_print(n);
return 0;
}
3.移位操作符
<< 左移操作符
>> 右移操作符
移位移位,移动的是二进制位
- 将一个数切换成二进制
- 再进行移位
2.1左移
移位规则:
左移:左边弃之,右边补0
ps:是b接收了
a<<1
的值,b为20,但是a还是原来的10,没有变化
2.2右移
移位规则:
- 逻辑移位
左边用0填充,右边丢弃
- 算术移位
左边用原该值的符号位填充,右边丢弃
ps1:咱们vs编译器的右移使用的是算术右移
警告⚠ :对于移位运算符,不要移动负数位,这个是标准未定义的。
4.位操作符
& //按位与 -- 同1为1,否则为0
| //按位或 -- 同0为0,反则为1
^ //按位异或 -- 相同为0,相异为1
注:他们的操作数必须是整数。
ps:位操作符,相对于二进制位的操作
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011 -- 3的补码
int b = -5;
//10000000000000000000000000000101
//11111111111111111111111111111010
//11111111111111111111111111111011 -- -5的补码
//a&b
//00000000000000000000000000000011 -- &之后的结构,符号位是0,原反补一致
//a|b
//11111111111111111111111111111011 -- |之后的结果,此时还是补码,打印出来要转换成原码
//10000000000000000000000000000100
//10000000000000000000000000000101 -- -5
//a^b
//11111111111111111111111111111000 -- ^之后的结果,此时还是补码,打印出来要转换成原码
//10000000000000000000000000000111
//10000000000000000000000000001000 -- -8
printf("a & b = %d\n", a & b);
printf("a | b = %d\n", a | b);
printf("a ^ b = %d\n", a ^ b);
return 0;
}
4.1&与>>的应用 – 计算出二进制下1的个数
解题思路:得到二进制下的每一位,如果是1,计数器count+1,最后count的值是二进制中1的个数
如何得到二进制中的每一位
通过&1,我们可以得到二进制中的最低位
再配合右移(>>),即可将二进位的每一位都得到
#include<stdio.h>
int main()
{
int a = 0;
while ((scanf("%d", &a)) != EOF)//多组测试用例,方便测试
{
int count = 0;
for (int i = 0; i < 32; ++i)
{
if ((a >> i) & 1)
{
++count;
}
}
printf("%d的二进制的1的个数:%d\n", a, count);
}
return 0;
}
4.2^的应用 – 不创建临时变量,交换两个数
根据相同为0,相异为1,^有这样的性质
性质1:0与一个数异或,得到的是这个数
0 ^ a = a
性质2:一个数与其本身异或,得到0
a ^ a = 0
性质1和性质2结合:
a ^ a ^ b = 0
b ^ b ^ a = 0
性质3:^具有交换律
a ^ b = b ^ a
所以可以得到:
a ^ b ^ a = b
a ^ b ^ b = a
即我们可以将a ^ b看成一个密匙,将密匙与a进行 ^运算得到b,将密匙与b进行 ^运算得到a
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
a = a^b;//a此时变成密匙
b = a^b;//密匙 ^ b = a b此时变成a
a = a^b;//密匙 ^ a = b a此时变成b
printf("%d %d\n", a, b);
return 0;
}
5.赋值运算符
= – 将右操作数赋给做操作数
例如:
int a = 10;
int x = 0;
int y = 20;
//也可以连续赋值
a = x = y+1;//从右向左赋值
//效果等价于
x = y+1;
a = x;
符合赋值符
+=
+=效果举例:
int x = 10;
x = x+10;
等价于:
x += 10;//复合赋值
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
6.单目操作符
! 逻辑反操作
- 负值
+ 正值 -- +a -- 对a不做任何处理
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
#include<stdio.h>
int main()
{
int a = 0;
int b = 7;
printf("!%d = %d\n", a, !a);
printf("!%d = %d\n", b, !b);
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
printf("%u\n", sizeof(int));
printf("%u\n", sizeof(a));
printf("%u\n", sizeof a);//true
//printf("%u\n", sizeof int); //error sizeof后面不接括号,不能接类型名,但是可以接变量名
printf("%d\n",~0);//-1 -- 二进制的每一位按位取反
return 0;
}
6.1前置,后置的++ –
前置:先使用,再++ –
后置:先++ – ,再使用
前置:
#include<stdio.h>
int main()
{
int a = 10;
//后置++
int b = a++;//先执行b = a ,再执行a++
printf("%d\n", b);//10
printf("%d\n", a);//11
return 0;
}
#include<stdio.h>
int main()
{
int a = 10;
//后置++
int b = ++a;//先执行a++,后执行b = a
printf("%d\n", b);//11
printf("%d\n", a);//11
return 0;
}
6.2sizeof和数组
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//(4)
}
int main()
{
int arr[10] = {
0};
char ch[10] = {
0};
printf("%d\n", sizeof(arr));//(1)
printf("%d\n", sizeof(ch));//(3)
test1(arr);
test2(ch);
return 0;
}
//(1) -- 40
//(2) -- 10
//(3) -- 4/8
//(4) -- 4/8
在函数调用test1
和test2
中,传过去的arr
和ch
数组名,是首元素地址的意义,即形参接收实参其实只是一个指针变量来接收,指针变量的大小只与平台有关,与类型无关
7.关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等” -- 注意是两个等号
8.逻辑操作符
&& 逻辑与
|| 逻辑或
&& 和 ||的理解有很多种
- 有些同学理解成both和either
- 有些同学理解成并且/都和或者
- 有些同学理解成电路中的串联和并联
大家用适合自己理解的去理解即可
举例1:
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
if (a && b)
{
printf("你们两都来啦\n");
}
else
{
printf("你们两怎么没都来");
}
return 0;
}
int main()
{
int a = 0;
int b = 0;
if (a || b)
{
printf("你们两至少有一个来了\n");
}
else
{
printf("你们两怎么没有一个人来\n");
}
return 0;
}
举例2:
int main()
{
int age = 0;
printf("请输入你的年龄\n");
scanf("%d", &age);
//if (18 <= age <= 36)//逻辑相差很大
if (age >= 18 && age <= 36)
{
printf("你是青年\n");
}
else
{
printf("你不是青年\n");
}
return 0;
}
这里比较容易错,大家如果想要表示一个18-36的范围,千万不要像数学中一样直接来一手
18 <= age <= 36
,这是一个错误的逻辑,&&是一个二元操作符,一次只能进行两个操作数的操作,无法达到我们一次到位的效果正确写法:
if (age >= 18 && age <= 36)
8.1&& 与 ||的短路问题
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;
}
修改:
#include <stdio.h>
int main()
{
int i = 1,a=0,b=2,c =3,d=4;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}
短路条件:
&&:当遇到假时,后面的表达式便不再执行
||:当遇到真时,后面的表达式便不再执行
9.条件操作符
下面的图片可以大致解释条件表达式的意思
龟龟小故事:小明和小刚同时爱上了小红,小红也不知道怎么选择,于是绝对让小明和小刚做一个竞争,
小明赢了,那么小红就选择小明,小明输了(小刚赢了),小红就选择小刚
代码举例:
#include<stdio.h>
int main()
{
int a = 3;
int b = 10;
int c = a > b ? a : b;
printf("c = %d\n", c);
return 0;
}
上述代码中,如果 a > b,则c = a,否则 c = b
正经解释:
条件表达式也叫做三目表达式,因为操作数有三个
exp1?exp2:exp3
,如果exp1
表达式的值为真,那么整个表达式的值就是exp2
的值,否则整个表达式的值就是exp3
的值
所以我们的求两数的最大值的函数可以稍微改善一下了:
改善前:
int Max(int x, int y)
{
if (x > y)
{
return x;
}
else
{
return y;
}
}
改善后:
int Max(int x, int y)
{
return x > y ? x : y;
}
龟龟小故事的结局与条件表达式无关😅
由于龟龟故事的结局有一点点大转变,审核警告我说我有点狂,所以大家可以去我仓库看龟龟小故事结局
10.逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。
ps:由于逗号表达式是所有表达式中,优先级最低的,咱们尽量加上括号
(exp1, exp2, exp3, …expN)
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a>b, a = b + 10, a, b = a + 1);
printf("%d\n", c);
return 0;
}
###11下标引用、函数调用和结构成员
-
[]
– 下标引用操作符语法:数组名 + [下标]
操作数:数组名 + 下标值
#include<stdio.h>
int main()
{
int arr[10];//创建数组
arr[9] = 10;//实用下标引用操作符。
//[ ]的两个操作数是arr和9。
printf("%d\n",arr[9]);
return 0;
}
-
()
– 函数调用操作符语法: 函数名 + (n个函数参数)
操作数:函数名 + 参数 (所以操作数至少有一个函数名)
#include <stdio.h>
void test1()
{
printf("你好\n");
}
void test2(const char *str)
{
printf("%s\n", str);
}
int main()
{
test1(); //实用()作为函数调用操作符。
test2("hello world");//实用()作为函数调用操作符。
return 0;
}
-
访问一个结构的成员
.
结构体.成员名->
结构体指针->成员名#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; }
12.操作符的属性
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级。
如果两者的优先级相同,取决于他们的结合性。
会控制求值顺序的操作符有:&& , || 逗号表达式,三目表达式
&& ||的短路会使每次的表达式求值顺序不一样
关于优先级,大家可以参考这张操作符优先级表
12.1编译器也无法知晓的表达式求值
ps:即使有了优先级,结合性,控制求值顺序这些属性,并不就意味着所有的表达式求值都有了一致的求法,代码的不规范,还是会造成编译器也不知该如何处理的情况
#include<stdio.h>
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
在《c和指针》这本书中,作者将改代码放在不同的编译器下跑的结果:
所以咱们还是要写出一些规范的代码,不然编译器也要一个头,两个大了
转载:https://blog.csdn.net/m0_64361907/article/details/127882415