栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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


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

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

文章目录
  • 1. 什么是操作符
  • 2. 操作符详解
    • 2.1 算术操作符☀️
        • `/ 除`
        • `% 求余`
    • 2.2 位移操作符☀️
      • 使用演示
        • `<< 左移操作符`
        • `>> 右移操作符`
    • 2.3 位操作符☀️
        • `& 按位与(同 1 为 1 )`
        • `| 按位或(有 1 为 1)`
        • `^ 按位异或(相同为 0,相异为 1)`
    • 2.4 赋值操作符☀️
        • `=`
        • `+=(此类是复合赋值操作符)`
    • 2.5 单目操作符☀️
        • `!逻辑反操作`
        • `+ 正值 - 负值`
        • `& 取地址`
        • `sizeof 取操作数类型的长度(以字节为单位)`
        • `~ 按位取反`
        • `++ -- `
        • `* 解除引用操作符`
        • `(数据类型) 强制类型转换`
    • 2.6 关系操作符☀️
    • 2.7 逻辑操作符☀️
        • `&&`
        • `||`
    • 2.8 条件操作符(三目操作符)☀️
    • 2.9 逗号表达式☀️
    • 2.10 下标引用操作符☀️
        • `[]`
    • 2.11 函数调用操作符☀️
        • `()`
    • 2.12 结构体成员访问操作符☀️
        • `.`
        • `->`
  • 3. 结尾


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
10000000000000000000000000000010 (原码) === -2
11111111111111111111111111111101 (反码)
11111111111111111111111111111110 (补码)
左移,左边丢弃,末尾补 0
11111111111111111111111111111100 (补码)
11111111111111111111111111111011 (反码)
10000000000000000000000000000100 (原码) === -4

>> 右移操作符

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

  1. 算术右移

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


正数

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

> 1" />


负数

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

> 1" />

  1. 逻辑右移

    右边丢弃,在左边补 0


正数

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

> 1 (逻辑右移)" />


负数

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

> 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 取操作数类型的长度(以字节为单位)

sizeof:sizeof(数据类型); 此时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("%dn", sizeof(a));
printf("%dn", sizeof a);

对于上面的代码,运行之后是这样的
所以

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("%dn", 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 < 20 或 x > 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 = %dn", max);

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

int a = 100;
int b = 40;
int max = 0;
max = a > b ? a: b;
printf("max = %dn", 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 = %dna = %dnc = %.2lfnmax = %dn", i, a, c, max);

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

逗号表达式

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


2.10 下标引用操作符☀️ []

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


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

然而 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 %dn", 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 %dn", z.name, z.age, z.hight, z.weight);

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

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


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

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


3. 结尾

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

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

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

撒花!!


最后 的 最后


求点赞  关注  评论  收藏 ⭐️ 啊!!
求点赞  关注  评论  收藏 ⭐️ 啊!!
求点赞  关注  评论  收藏 ⭐️ 啊!!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/648275.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号