【C语言初阶】[万字] 你见过这样用的操作符吗?这里可能有你不知道的操作符知识~ [ C语言操作符详解 一 ]

本文详细介绍了C语言中的各种操作符,包括算术、位移、位、赋值、单目、关系、逻辑、条件、逗号、下标引用、函数调用和结构体成员访问操作符。特别强调了操作符的使用规则和注意事项,如位移操作符的左右移、位操作符的与或异或、赋值操作符的复合使用、三目操作符以及逗号表达式。此外,还讨论了操作符的优先级和结合性,帮助读者深入理解C语言的操作符机制。
摘要由CSDN通过智能技术生成


这是C语言初阶的第三篇文章,今天就让我们来详细解读一下,C语言中的操作符!!
一起来看看吧!

(友情提示:本篇篇幅也许过长,最好不要跳跃式浏览)


1. 什么是操作符💦

🌀在C语言中,操作符可以分为 🌀

📌
算术操作符 ☀️

  • + - * / %

位移操作符☀️

  • >> <<

位操作符☀️

  • &|^

赋值操作符☀️

  • = += -=*= /= &= ^= |= >>= <<=

单目操作符☀️

  • ! - + & sizeof ~ -- ++ * (数据类型)

关系操作符 ☀️

  • > >= < <= != ==

逻辑操作符 ☀️

  • && ||

条件操作符(关系操作符) ☀️

  • exp1 ? exp2 : exp3

逗号表达式 ☀️

  • exp1 , exp2 , exp3, exp4 ……

下标引用操作符 ☀️

  • []

函数调用操作符☀️

  • ()

结构体成员访问操作符☀️

  • . ->

没错,在C语言中从 加+ 减- 乘* 除/,到访问数组所需要的 [] 统统都属于操作符。
🌊那么这些操作符究竟该如何使用呢?它们的使用有什么需要注意的地方吗?下面就让我来给大家一 一详细的介绍一下!
冲鸭!!!🌊🌊🌊


2. 操作符详解💧


2.1 算术操作符☀️

算术操作符

算术操作符中的 + - * 就与我们平常计算时候一样,没有什么需要太需要注意的点🐥。

/ 除

除号两边操作数的类型不同,执行的操作也有一定的区别👇

📌
如果两边操作数

都为整数,执行整数除法

🌸
除号两边都是整数

如果两边的操作数

有一个是浮点数,执行浮点数除法

🌸
除号两边有一个浮点数

% 求余

📌
求余符号的两边

必须都是整数才能进行运算

🌸
%正确的使用
这是正确的使用方法,但是如果操作数有浮点数出现,那么就会…… 👇%错误使用
编译都无法通过!!!!!!!


2.2 位移操作符☀️

位移操作符
位移操作符

只能操作整型数据

而且操作整型在内存中存储的二进制数据!

根据我们在之前整型提升的博客中我们学习到,整型在内存中存储的形式是二进制的补码表现形式,所以位移操作符操作的就是👇
整型补码

📌
整型提升博客指路:

【C语言初阶】 为什么我的两个整数加起来结果不对?原来是你,整型提升~

使用演示

<< 左移操作符


整数的二进制补码
向左位移,左边丢弃,末尾补 0

📌
正数

2 << 1
00000000000000000000000000000010 (补码) === 2
00000000000000000000000000000100 (补码) === 4

2 << 1

📌
负数

-2 << 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
左移,左边丢弃,末尾补 0
11111111111111111111111111111100 (补码)
11111111111111111111111111111011 (反码)
10000000000000000000000000000100 (原码) === -4

-2 << 1

>> 右移操作符

右移操作符,有两种位移方式,相对来说有些复杂(并且,两种位移方法取决于编译器和用法没有关系)

  1. 算术右移

    右边丢弃,在左边补原符号位

📌
正数

2 >> 1
00000000000000000000000000000010 (补码) === 2
右移,右边丢弃,左边补 符号位
00000000000000000000000000000001 (补码) === 1

2 >> 1

📌
负数

-2 >> 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
右移,右边丢弃,左边补 符号位
11111111111111111111111111111111 (补码)
11111111111111111111111111111110 (反码)
10000000000000000000000000000001 (原码) === -1

-2 >> 1

  1. 逻辑右移

    右边丢弃,在左边补 0

📌
正数

2 >> 1
00000000000000000000000000000010 (补码) === 2
右移,右边丢弃,左边补 符号位
00000000000000000000000000000001 (补码) === 1

2 >> 1 (逻辑右移)

📌
负数

-2 >> 1
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
右移,右边丢弃,左边补 0
01111111111111111111111111111111 (补码) === (原码) === 2147483647

-2 >> 1 (逻辑右移)

(PS:操作符使用时,无论是 << 左移操作符 ,还是 >> 右移操作符

不会改变操作数的原值

即:int a = 2; int b = a >> 1; 两个语句执行之后 a 的值不变)


2.3 位操作符☀️

位操作符
位操作符同位移操作符的操作数一样
只能操作整数,且操作二进制的补码

& 按位与(同 1 为 1 )

什么叫同 1 为 1?直接上例子👇

📌

3 & 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
同 1 为 1
00000000000000000000000000000001 (补码) === 1

📌

-2 & 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
同 1 为 1
00000000000000000000000000000010 (补码) === 2

从上边的例子我们可以看出 同 1 为 1,就是 两数二进制位的相同位置都为 1 时,结果的二进制位对应位置为 1

包括符号位


| 按位或(有 1 为 1)

按照相同的规律👇

📌

3 | 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
有1 为 1
00000000000000000000000000000111 (补码) === 7

📌

-2 | 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
有 1 为 1
11111111111111111111111111111110 (补码) === -2

^ 按位异或(相同为 0,相异为 1)

📌

3 ^ 5
00000000000000000000000000000011 (补码) === 3
00000000000000000000000000000101 (补码) === 5
相同为 0,相异为 1
00000000000000000000000000000110 (补码) === 6

📌

-2 ^ 2
11111111111111111111111111111110 (补码) === -2
00000000000000000000000000000010 (补码) === 2
相同为 0,相异为 1
11111111111111111111111111111100 (补码) === -4

希望大家不要眼花缭乱啦!!!


2.4 赋值操作符☀️

赋值操作符

=

将右值(右变量的值)赋予左变量👇

int a = 10;
int b = a;
则
a === 10;
b === 10;

赋值操作符可连续赋值👇

int a = 5;
int b = 0;
int c = 10;
a = b = c + 1;
则
a === 11;
b === 11;
c === 10;
//但是这样的写法,可读性差,也不利于调试,所以最好不要这样写
b = c + 1;
a = b;
//这种写法可以实现同样的效果,但是可读性好,且利于调试
+=(此类是复合赋值操作符)
a += 1;		==>	  a = a + 1;

其他的赋值操作符只需要注意除等号外操作符的特点就好了!!


2.5 单目操作符☀️

目,就是指操作数的个数
单目,就是只有一个操作数
单目操作符

!逻辑反操作

在C语言中

0 为假, != 0 为真

我们就可以用下面的例子来解释 !
示例

我们看到
第一个判断条件,
如果为真,输出 !!!
如果为假,输出 ~~~
第二个判断条件
如果为真,输出000
如果为假,输出666

两个判断,输出的结果是
!!!666
判断出 !0 为 真,!1 为 假
所以 ! 的作用就是👇

将假 逻辑反为真,将真 逻辑反为假


+ 正值 - 负值

-

将正数变为负数,负数变为正数


& 取地址

&:可以取出变量的地址

int a = 10;
&a;	//这样就取出了 变量 a 的地址

但是 不可以对常量取地址
🌸

&1;
&2;
&3;
//这些都是不可以的

sizeof 取操作数类型的长度(以字节为单位)

sizeofsizeof(数据类型); 此时sizeof 的值就是 所操作数据类型的长度

int a = 10;
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int b = sizeof(a);
int c = sizeof(arr);
则
b === 4;
c === 40;
//计算此数组类型的长度时,数组类型为 int [10] 即 长度为 10 的整型数组

虽然 sizeof 的使用,我们一般会加上 ()

但是sizeof 并不是一个函数

int a = 0;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);

对于上面的代码,运行之后是这样的👇sizeof不是函数的证明
所以

sizeof(a) <==> sizeof a
sizeof 只是一个操作符,并不是函数


~ 按位取反

只能操作整数
且操作二进制的补码

📌

~(-2)
11111111111111111111111111111110 (补码) === -2
按 位 取 反
00000000000000000000000000000001 (补码) === 1

++ --

++
++ <==> += 1

++ 是 自增 1
所以 -- 就是 自减 1

但是前置、后置还有区别👇

📌
前置 ++(--)

++a;(--a;) 
//表示 先自增(自减),后使用

示例

前置++,先自增,后使用
a 先 自增 1,后赋值于 b
所以 a === 1; b === 1;

📌
后置 ++(--)

a++;(a++;) 
//表示 先使用,后自增(自减)

在这里插入图片描述

后置++,先使用,后自增
a 先 赋值于 b,后 自增 1
所以 a === 1; b === 0;

🌸,只举了 ++ 的例子,但是 -- 是相同的,希望大家可以理解 ++ -- 的用法


* 解除引用操作符

这不是 算术操作符中的 乘* 虽然字符相同,但是用在不同的地方,是不同的操作符
*
一般用于

对指针解引用

📌

int a = 100; 
int *pa = &a;
*pa = 20; 
printf("%d\n", a);

示例
上述代码编译运行可见,我们通过 *(解引用操作符) 对指向 a 的指针 pa 解引用 来改变了 a 的值。
也就是说 pa 指向了 a 的地址,我们通过对 pa 解引用就找到了 a 的值
这就是 *(解引用操作符) 的作用,因为用 * 可以通过 pa 间接来找到 a,所以 * 也被称作

间接访问操作符


(数据类型) 强制类型转换

(数据类型)
顾名思义,就是可以强制转换数据的类型
用法👇

📌
大家先来看一组代码:

double a = 0; 
a = 12 / 5; 
printf("%lf", a); 

上述代码
警告

上述代码,执行之后发现,在编译的过程中编译器提出了警告,而且a 虽然是 double 类型,但是因为参与运算的两个数是 整型,
所以

实际存入 a 的值还是整型,这样就造成了精度的缺失

这时候我们可以用 (double) 来强制类型转换

double a = 0; 	
a = 12 / (double)5; 	
printf("%lf", a); 

上述代码
这个时候就解决了精度缺失的问题
因为 (double) 强制将 整型的 5 转换成了 double 型的 5.0
所以运算结果就成了 double

对于 强制类型转换 还需要注意一点👇

📌
对于一下这样的代码,大家想一想有没有什么问题👇

int a = 0; 
(int*) pa = &a; 

这样的代码,在定义变量的时候,将类型用 () 将 int* 指针类型括了起来,好像更能说明 * 是和 int 结合的 表示 int* 类型的指针

还有这样的代码👇

int a = int(3.14); 

在强制类型转换的时候,将需要转换的 数值 用 () 括了起来

这两种代码都是有问题的!

对于第一种:

如果在定义的时候 将 类型 用 () 括起来了,那么 就会表示强制类型转换,就起不到定义变量类型的作用了

对于第二种:

()强制类型转换操作符 只能对类型使用,不能对数值使用


2.6 关系操作符☀️

关系操作符
关系操作符需要注意的点没有太多,主要用于作为判断条件时的使用👇

if(a < b);
while(a < b);

一般用于以上的 if 或者 while 语句中
表达式成立,则为真 === 1
不成立,则为假 === 0

(注意:在使用 == 判断相等时,千万不要写成 =(赋值操作符)


2.7 逻辑操作符☀️

逻辑操作符
逻辑表达式的 &&(逻辑与) ||(逻辑或)只关注表达式的真假!

和位运算符中的 &(按位与) |(按位或),操作二进制位!

千万不要
搞混了!!!!


&&

逻辑与 怎么用呢?👇

📌
1.表示并且

int age = 0; 
scanf("%d", &age); 
if(age > 0 && age < 18) 
{//我们可以用 &&(逻辑与) 来控制判断条件的范围,表示并且  
 // age > 0 并且 age <18  
 // 但是千万不能写成 0 < age < 18 	
	printf("minors");
} 

只要表达式有一个为假,则判断条件就为假
正常写法
上边代码,我们编译运行之后,是非常正确的
但如果我们把条件写成 0 < age < 18 这样的形式呢?
错误写法
我们会发现尽管输入了不在范围内的数值,他还是输出了 minors
这是因为 < 是逻辑操作符

只关注表达式的真假

上述判断条件,在判断的时候,0会先与 age 比较
如果为真 0 < age === 1
如果为假 0 < age === 0
但 无论是 0 还是 1,都比 18 要小,所以 0 < age < 18 一定为真
这就造成了错误的结果,所以

需要控制判断变量在一个范围内时
一定不要连用 逻辑操作符

📌
2.左表达式不成立,右表达式不计算

大家看一下以下代码会输出什么呢?

//代码 1
int a = 0;
int b = 1;
int c = 2;
int i = 0;
i = a++ && ++b && ++c;
printf("%d %d %d", a, b, c);

这个代码又会输出什么呢?

//代码 2
int a = 0; 
int b = 1; 
int c = 2;
int i = 0;
i = ++a && ++b && ++c; 
printf("%d %d %d", a, b, c); 

代码 1:输出 1 1 2
代码 2:输出 1 2 3
那为什么会出现这种情况呢?
是因为 &&(逻辑与) 有一个规定,就是

左表达式不成立,右表达式不计算

首先 a === 0;

a++ 表示先使用 a 再自增,所以 a++ && ++b && ++c 中,最左端表达式 为假,右边的两个表达式不计算。就出现了 a === 1,b === 1,c ===2的情况

++a 表示先自增 再使用a,所以 ++a && ++b && ++c 中,所有表达式都为真,所有表达式都进行计算。就出现了 a === 1, b === 2, c === 3的情况

如果理解了第二个规定,表示并且 也可以用这个规定解释!


||

📌
1.表示或者
&&逻辑与 相同,可以用来控制 判断条件的范围👇

int x = 0; 
scanf("%d", &x); 
if (x < 20 || x > 40) 
{
	printf("didi"); 
} 

这表示,x < 20x > 40 时,打印 didi

只要表达式有一个为真,则判断条件就为真
逻辑或

📌
2.左表达式成立,右表达式不计算
逻辑或的判断逻辑,正好与逻辑与相反

//代码 1  
int a = 0;   
int b = 1;   
int c = 2;  
int i = 0;  
i = a++ || ++b || ++c;   
printf("%d %d %d", a, b, c); 
//代码 2  
int a = 0;   
int b = 1;   
int c = 2;  
int i = 0;  
i = ++a || ++b || ++c;   
printf("%d %d %d", a, b, c);  

代码 1:输出 1 2 2
代码2:输出 1 1 2
这就说明了,||(逻辑或) 的规定

左表达式成立,右表达式不计算

首先 a === 0

a++ 表示先使用 a 再自增,所以 a++ && ++b && ++c 中,最左端表达式 为假,第二个表达式为真,第三个表示不计算。就出现了 a === 1,b === 2,c ===2的情况

++a 表示先自增 再使用 a,所以 ++a && ++b && ++c 中,最左端表达式 为真,后两个二个表达式不计算。就出现了 a === 1,b === 1,c ===2的情况


2.8 条件操作符(三目操作符)☀️

条件操作符
使用三目操作符可以省去一些冗余的代码

int a = 100;
int b = 40;
int max = 0;
if (a > b)
	max = a;
else
	max = b;
printf("max = %d\n", max);

上边的代码,可以判断 ab 谁大
我们可以用三目操作符直接写成👇

int a = 100;
int b = 40;
int max = 0;
max = a > b ? a: b;
printf("max = %d\n", max);

两端代码最终的结果都是👇
最终结果

📌
所以对于条件操作符 exp1 ? exp2 : exp3

exp1 为真,则整个表达式的结果为 exp2
exp1 为假,则整个表达式的结果为 exp3


2.9 逗号表达式☀️

逗号表达式
先来看一组代码👇

int max = 0;
int i = 2;
int a = 7;
double c = 21.3;
max = ( i++, a--, c *= 10, 55 / 11);
printf("i = %d\na = %d\nc = %.2lf\nmax = %d\n", i, a, c, max);

代码示例

上述代码,所有变量都经过计算改变了值,但输出 max 的值为 55 / 11 === 5;
所以

逗号表达式👇

从左向右依次计算,整个表达式的结果是最后一个表达式的结果


2.10 下标引用操作符☀️

[]

一般用于数组下标的访问
int arr[5] = { 1,2,3,4,5 };
arr[4]中 ,[]是操作符,arr4 是操作数

📌
我们都知道,除了某些特定情况,数组名就是数组首元素的地址
所以 arr[4] <==> arr + 4

然而 arr + 4 <==> 4 + arr
那么可不可以将 arr[4] 写成 [4]arr呢?
实操一下!👇
 arr[4]和 4[arr]
从结果来看,我们的推断是可以的
那么这说明什么呢?

这说明 [] 就只是一个操作符,只要符合语法,操作数的顺序是可以颠倒的


2.11 函数调用操作符☀️

()

函数调用操作符,顾名思义就是再调用函数时侯使用的操作符
对于

Test();		//操作数:Test
Add(2,3);	//操作数:Add,2,3

2.12 结构体成员访问操作符☀️

.

使用方法
结构体变量名 . 结构体成员名

🌸

struct Student
{
	char name[100];
	int age;
	int hight;
	int weight;
};
struct Student z = { "zhangsan",18,175,130 };
printf("%s %d %d %d\n", z.name, z.age, z.hight, z.weight);

上述代码
我们可以分别输出 姓名 年龄 身高 体重
说明我们用 . 访问量结构体内的成员


->

使用方法
结构体指针 -> 结构体成员

🌸

struct Student
	{
		char name[100];
		int age;
		int hight;
		int weight;
	};
	struct Student z = { "zhangsan",18,175,130 };
	printf("%s %d %d %d\n", z.name, z.age, z.hight, z.weight);

	struct Student* pz = &z;
	printf("%s %d %d %d\n", pz->name, pz->age, pz->hight, pz->weight);

-> 操作符
可以看出,使用 结构体指针 -> 结构体成员,也可以访问结构体成员


以上就是C语言中所有的操作符啦!

在任何语言中,操作符都是非常重要的一部分。如果不能对操作符有着熟练、深刻的理解,那么就不可能熟悉任何一门语言!
如果不能熟练掌握、深刻理解操作符的使用,那么对于数据的处理,肯定是不行的。


3. 结尾🌺

本篇关于 C语言中操作符的详细解释就已经结束啦!!

但是关于操作符的使用,在我们写代码的时候还会有一些陷阱出现,明天我会把类似的一些陷阱 或者 使用的错误,以及 操作符的优先性、结合性统一讲解一下!

OK👌!本篇博客到此就结束啦!!!
如果有写的不好的地方,欢迎大家提出宝贵的建议或者意见。

撒花!!🌺🌺

在这里插入图片描述


最后 的 最后 🌹

在这里插入图片描述
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!
求点赞 👍 关注 🌟 评论 🔥 收藏 ⭐️ 啊!!
在这里插入图片描述

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈米d1ch

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值