操作符详解

本人能力有限,难免有叙述错误或者不详细之处!希望读者在阅读时可以反馈一下错误以及不够好的地方!感激不尽!

目录

算数操作符

位移操作符

位操作符

赋值操作符

复合赋值操作符:

单目操作符

关系操作符

逻辑操作符

 条件操作符

逗号表达式

[ ] 下标引用操作符,->操作符

隐式类型转换

算术转换


在C语言中,操作符的功能以及使用还是非常重要的,以下则是部分操作符的详解。

算数操作符

    +        -       %       /          * 

分别为加法,减法,取余数,除法,乘法。


一些细节:

1.以上几个操作符中,除了%以外,其他的操作符的操作数都可以是整数或者浮点数,只有%的操作数只能是整数。% 操作符的两个操作数必须为整数。返回的是整除之后的余数。

2. 3/2等于多少?不是想当然的1.5,而是1,当/左右两边的操作数为整数时执行整数除法,而是浮点数的时候才执行浮点数除法,也就是3/2.0才能得到1.5.

位移操作符

<< 左移操作符

>> 右移操作符


位移操作符移动的是二进制位,只能作用于整数。

在介绍这有关于位运算的操作符之前应该先了解内存中整数的存储方式:

1.正整数

对于正整数来讲,它的原码,补码,反码都是一样的。

只要放到内存里的数值都是以补码形式存放的。

原码是什么?

在内存中存储数据时的使用的是二进制,以整型为例,int num = 10时,10在内存中的存储情况时这样的:

int 10 = 00000000000000000000000000001010//二进制表示

转换为十进制即从右向左一次为0*2的0次方,1*2的1次方,0*2的2次方,1*2的3次方,0*2的4次方……依次相加后得到的结果。

因为10是一个正整数所以对于10来说它的原码反码补码如下:

原码:00000000000000000000000000001010

反码:00000000000000000000000000001010

补码:00000000000000000000000000001010

2.负整数

而对于负整数来说,它的正反补码则不一样。

首先,为了表示一个负数,符号位是最高的一位,则-10的原码如下:

int -10 = 10000000000000000000000000001010//二进制表示

负数的反码就是在其原码的基础上 符号位不变 其他位取反。

int -10 = 10000000000000000000000000001010//原码
int -10 = 11111111111111111111111111110101//反码

负数的补码就是在其反码的基础上+1

-10 = 11111111 11111111 11111111 11111011//-10的补码,也就是内存中真正存放-10的状态

左移操作符:

而左移操作符的效果如下,以10为例:

左边抛弃,右边补0。

00000000000000000000000000001010

num<<1

00000000000000000000000000010100

右移操作符:

右移操作符右以下两种位移:

1. 逻辑移位
左边用0填充,右边丢弃
2. 算术移位
左边用原该值的符号位填充,右边丢弃,左边补原符号位,正的补0,负的补1 vs2019选择的是算数右移

其作用原理规则如上,效果不做演示。

注意!对于移位运算符,不要移动负数位,这个是标准未定义的!

int =10;
num>>-1;//error

位操作符

& //按位与
| //按位或
^ //按位异或

注意!他们的操作数必须是整数。

& - 按二进制位与: 1& 1 = 1 ;1& 0=0;0&0 = 0

| - 按二进制位或:     1|1=1   ; 1|0=1  ;0|0 =0

^ - 按二进制位异或:相同为0,相互异为1,

这些位移操作符看上去好像没什么用,但是如果我们碰到了如下的一道题呢?

不创建临时变量交换两个数:

这个时候我们就可以巧妙的运用以下位移操作符了

int main()
{

int a = 3;
int b = 5;

printf("交换前:a=%d b=%d\n", a, b);

  	a = a ^ b;   //a=3^5
   	b = a ^ b;   //3^5^5 --> b=3
    a = a ^ b;   //3^5^3 --> a=5

printf("交换后:a=%d b=%d\n", a, b);

return 0;
}

赋值操作符

=

也就是我们平常经常接触的操作符=了。

其更多的功能不做过多细述,不过当连续赋值时其顺序依然要记录一下:

a = x = y-1;
同等效于:
x = y-1;
a = x ;

但是连续赋值的写法非常不讨喜,难以阅读和更改维护,所以请尽量避免。

复合赋值操作符:

%=

*=

/=

-=

+=

>>=

<<=

&=

^=

|=

其主要作用主要是为了简洁代码,其效果举例如下:

int x = 0;
int y = 10;
x = x + y;
等效于:x += y;

注意!赋值运算符本身的优先级是比较低的,当然复合运算符也是一样的!

以下的陷阱代码还请留心:

int x = 1;
int y = 2;
x *= y + x; 

由于+的优先级高于复合赋值运算符,x其实会等于:x = 1*(1+2)

单目操作符

!                                逻辑反操作
-                                  负值
+                                 正值
&                                 取地址
sizeof                          操作数的类型长度(以字节为单位)
~                                 对一个数的二进制按位取反
--                                 前置、后置--
++                               前置、后置++
*                                  间接访问操作符(解引用操作符)
(类型)                          强制类型转换

其中大部分的操作符作用我已在前面的文章有过记述:C语言的部分操作符概述与static概述_lanload的博客-CSDN博客

关系操作符

>
>=
<
<=
!=                         用于测试“不相等”
==                         用于测试“相等”

不做过多细述。

逻辑操作符

&&                         逻辑与
||                            逻辑或


一些细节:

&&左边为假,右边就不计算了

||左边为真,右边就不计算了

条件操作符

exp1?exp2:exp3

int r = a>b?a:b

A是否大于B?如果大于取A,小于取B

其本质是,?前的表达式为真,取表达式2;?前表达式为假,取表达式3。

逗号表达式

exp1,exp2,exp3,.......expn

逗号表达式,就是用逗号隔开的多个表达式。
逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果

请注意!执行是执行,取结果是取结果,请不要弄混了两者的概念而只是去执行最后一个逗号表达式!

[ ] 下标引用操作符,->操作符

[ ]

通常应用于数组

arr[7] == 7[arr]

arr[7] ---->*(arr+7),arr是首元素的地址,其实是等价的

->

左边是结构体指针,箭头指向结构体内的成员,使用箭头的时候不需要解应用符号*

ps->age等价于(*ps).age

隐式类型转换

我们知道,Char类型的数据存储只有8个比特位,但是C语言中进行整型算术运算总是至少以缺省整型类型的精度来进行,char类型的本质依然是整型,所以在一些情况下,会涉及到隐式类型转换也就是整型提升的情况。

隐式类型转换是发生在程序运行时的转换,平常情况下我们时无法观察到的,这种转换的发生情况一般是计算char类型或者短整型相加时

char a,b,c;

a = b + c;

整型提升,简单来讲,CPU进行整型运算字符型和短整型时,因为CPU内部的运算硬件和寄存器的长度要求都是int长度的,所以,CPU计算字符型时,会把char类型整型提升膨胀到32位进行计算。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度才能计算。

整型提升到底是怎样的?

进行整型提升时,补充至32位的数值是由符号位决定的,比如-1的整型提升如下

负数的整型提升:

char a = -1;
char类型内存存储-1时的情况:
                        11111111(补码)
11111111111111111111111111111111(整型提升后)
由符号位补充至32位.

正数的整型提升:

char a = 1;
char类型内存存储1时的情况:
                        00000001(补码)
00000000000000000000000000000001(整型提升后)
由符号位补充至32位.

当无符号类型整型提升时,补充至32位的是0

整型提升的例子:

char a = 0xb6;
short b = 0x11b6;
int c = 0xb6000000 ;
if(a==0xb6)
printf("a");

if(b==0xb600)
printf("b");

if(c==0xb6000000)
printf("c");

以上的输出结果是:c

其中,ab发生了整型提升,因为其变量类型已经无法容下其能容纳的数值大小,在发生了整型提升后其值变成了负数,不能满足if的条件。

为什么会变成负数?以a为例

有符号的char类型的取值范围是-128~127,a转换为2进制后的值远远超出了这个范围,这个时候触发了整型提升,我们先写出来a的二进制序列:1011 0110

a是一个char类型的数据,我们可以看到这个时候数据其实因为超出了范围将符号位变成了1,整个数值变成了负数。

至于更多的细节,如转换成十进制后的数值,我们将在之后的数据存储中讨论它。

第二个例子:

int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}

 只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字
节.
表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类

型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long double
double
float
unsigned long int
long int
unsigned int
int

 发生于数据类型不同时。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛使用的编程语言,它具有高效、灵活、可移植性强等特点,被广泛应用于操作系统、嵌入式系统、数据库、编译器等领域的开发。C语言的基本语法包括变量、数据类型、运算符、控制结构(如if语句、循环语句等)、函数、指针等。在编写C程序时,需要注意变量的声明和定义、指针的使用、内存的分配与释放等问题。C语言中常用的数据结构包括: 1. 数组:一种存储同类型数据的结构,可以进行索引访问和修改。 2. 链表:一种存储不同类型数据的结构,每个节点包含数据和指向下一个节点的指针。 3. 栈:一种后进先出(LIFO)的数据结构,可以通过压入(push)和弹出(pop)操作进行数据的存储和取出。 4. 队列:一种先进先出(FIFO)的数据结构,可以通过入队(enqueue)和出队(dequeue)操作进行数据的存储和取出。 5. 树:一种存储具有父子关系的数据结构,可以通过中序遍历、前序遍历和后序遍历等方式进行数据的访问和修改。 6. 图:一种存储具有节点和边关系的数据结构,可以通过广度优先搜索、深度优先搜索等方式进行数据的访问和修改。 这些数据结构在C语言中都有相应的实现方式,可以应用于各种不同的场景。C语言中的各种数据结构都有其优缺点,下面列举一些常见的数据结构的优缺点: 数组: 优点:访问和修改元素的速度非常快,适用于需要频繁读取和修改数据的场合。 缺点:数组的长度是固定的,不适合存储大小不固定的动态数据,另外数组在内存中是连续分配的,当数组较大时可能会导致内存碎片化。 链表: 优点:可以方便地插入和删除元素,适用于需要频繁插入和删除数据的场合。 缺点:访问和修改元素的速度相对较慢,因为需要遍历链表找到指定的节点。 栈: 优点:后进先出(LIFO)的特性使得栈在处理递归和括号匹配等问题时非常方便。 缺点:栈的空间有限,当数据量较大时可能会导致栈溢出。 队列: 优点:先进先出(FIFO)的特性使得

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值