文章目录
- 1:算术操作符
- 2:移位操作符(移动的是二进制序列中的补码)
- 2.1:知识补充(原码,反码,补码与二进制)
- 2.2:左移操作符(<<)
- 2.2:右移操作符(>>)
- 2.2.1:逻辑右移
- 2.2.2:算术右移
- 3:位操作符(运算用的是二进制位的补码)
- 3.1:按位与操作符(&)
- 3.2:按位或操作符(|)
- 3.3:按位异或操作符(^)
- 4.赋值操作符
- 4.1:复合赋值符
- 5:单目操作符
- 5.1:逻辑反操作符(!)
- 5.2:正值,负值操作符(+ -)
- 5.3:取地址操作符和解引用操作符(& *)
- 5.4 sizeof操作符
- 5.5:按位取反操作符(操作的是补码)
- 5.6:++,--操作符
- 5.7:强制类型转换操作符
- 6:关系操作符
- 7:逻辑操作符
- 7.1:逻辑与操作(&&)
- 7.2:逻辑或操作(| |)
- 8.条件操作符(三目操作符)
- 9:下标引用、函数调用和结构体成员访问操作符
- 9.1:下标引用
- 9.2:函数调用操作符
- 9.3:结构体成员访问
- 10:操作符的属性
- 10.1优先级
- 10.2结合性
- 11:表达式求值
- 11.1:整型提升
- 11.1.1整型提升的意义:
- 11.1.2:如何进行整型提升
- 11.2:算术转换
- 11.3:问题表达式解析
- 11.3.1:表达式1
- 11.3.2:表达式2
- 11.3.3:表达式3
嘻嘻,家人们,今天咱们来详解C语言中的操作符,好啦,废话不多讲,开干!
1:算术操作符
算术操作符呢其实就是我们日常生活中常见的加减乘除还有取余,这里博主就不具体展开讲了,就针对这些操作符中的小注意事项来进行讲解。
+ - *(乘法) /(除法) %(取余)
- (1):除了%操作符以外,其他的几个操作符都可以作用于整数和浮点数。
- (2):对于/操作符,如果两个操作符都为整数,则执行整数除法(即只保留整数部分,去除小数部分),而只要存在浮点数执行的就是浮点数除法。
- (3):%操作符的两个操作数必须为整数,返回的是整除之后的余数。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{//得到的是小数,但是是放入整型数据,因此得到的是3printf("7 / 2 = %d\n", 7 / 2);printf("7.0 / 2.0 = %d\n", 7.0 / 2.0);/**只要浮点数存在,那么执行的就是浮点数除法*/printf("7.0 / 2 = %lf\n", 7.0 / 2);printf("7.0 / 2.0 = %lf\n", 7.0 / 2.0);//%操作符的两个操作数必须为整数,返回的是整数之后的余数printf("7 %% 2 = %d\n", 7 % 2);return 0;
}
2:移位操作符(移动的是二进制序列中的补码)
2.1:知识补充(原码,反码,补码与二进制)
在讲移位操作符之前,首先博主得补充些小知识,原码,反码和补码与二进制的相关知识
在日常生活中我们经常能够听到2进制、8进制、10进制、16进制的讲法,那是什么意思呢?其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。拿具体的数字15来举例。
(1):15的2进制:1111
(2):15的8进制:17
(3):15的10进制:15
(4):15的16进制:F
博主重点介绍二进制,首先还是得从10进制讲起,10进制就是我们在日常生活中经常使用的,10进制数字有这两种特点:
(1):10进制中满10进1
(2):10进制的数字每一位都是0~9的数字组成。
那么其实二进制也是一样的,
(1):二进制中满2进1。
(2):二进制的数字每一位都0~1的数字组成。
了解了二进制相关的概念后,接下来博主将讲解原码、反码、补码
整数的2进制表示方法有三种,即原码,反码和补码
三种表示方法均有符号位和数值位两部分,符号位都是用0表示"正",用1表示"负",而数值位最高位的一位是被当做符号位,剩下的都是数值位。
正整数的原、反、补码都相同
负整数的三种表示方法各不相同
(1):原码:直接将数值按照正负数的形式翻译成⼆进制得到的就是原码。
(2):反码:符号位不变,其他位依次按位取反就可以得到反码。
(3):补码:反码+1就得到补码。
对于无符号数整数来说,没有符号位,所有位均位有效位。
2.2:左移操作符(<<)
移位规则:左边丢弃,右边补0
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int m = 7;int n = m << 1;printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}
上面这段代码,将变量m左移一位之后然后赋值给n,7是正整数,因此原码,反码,补码相同,且int类型占四个字节,一个字节是8位,因此是32位。
7的补码:0000 0000 0000 0000 0000 0000 0000 0111
左移一位之后的补码:0000 0000 0000 0000 0000 0000 0000 1110
打印的时候,打印的是原码,而正整数的原码反码补码相同,因此此时将左移之后的补码转换为10进制数之后就是14,因此n = 14;
看完上面的例子后,咱们再看一个负整数的例子。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;/* * 左移一位之后补码:11111111111111111111111111110010反码:11111111111111111111111111110001原码:10000000000000000000000000001110*/int n = m << 1;printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}
此时,我将m赋值为-7,然后左移一位后赋值给n,在之前我们了解过,对于负整数,我们要求出补码,首先对原码取反得到反码,反码再+1得到补码,接着再左移一位(左边丢弃,右边补0)赋值给n,由于在输出打印的时候,是以原码的形式打印的,因此我们还需要求出左移之后的原码,根据计算可得,-7在左移一位之后得到的是-14,因此n的值为-14。
2.2:右移操作符(>>)
移位规则:首先算数右移分两种:
- 1.逻辑右移:左边用0填充,右边丢弃。
- 2.算术右移:左边用原该值的符号位进行填充,右边丢弃。
2.2.1:逻辑右移
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;int n = m >> 1;/*补码:01111111111111111111111111111100反码:01111111111111111111111111111011原码:00000000000000000000000000000100*/printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}
如果是按照逻辑右移的方式来进行移位的话,-7在右移一位之后,应该会变成4。
2.2.2:算术右移
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{/*-7原码:10000000000000000000000000000111反码:11111111111111111111111111111000补码:11111111111111111111111111111001*/int m = -7;int n = m >> 1;/*补码:11111111111111111111111111111100反码:11111111111111111111111111111011原码:10000000000000000000000000000100*/printf("m = %d\n", m);printf("n = %d\n", n);return 0;
}
若按照算术右移的方式,在右边补上符号位,左边丢弃,在上面我们讲解过,对于有符号整数,最高位为符号位,0代表正,1代表负,因此-7在进行算术右移的时候应该补1,按照算术右移的方式,-7在算术右移一位之后,应该会变成-4。
通过观察结果可知,博主使用的vs2022在这里采用的是算术右移,因此n的是为-4。
(1)对于移位操作符,不要移动负数位,这个标准是未定义的。
(2)移位操作符的操作数只能是整数。
3:位操作符(运算用的是二进制位的补码)
位操作符有三种,分别是
> 按位与--------- &
按位或--------- |
按位异或------ ^
我们分别来看这三个操作符。
3.1:按位与操作符(&)
首先是按位与操作符,按位与操作符用一句话来总结就是**"同时为1则1",**可能家人们对这句话不是特别理解,我们来看一段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a & b;printf("%d\n", c);return 0;
}
将5和-3进行按位与操作然后赋值给c,此时c的值为5,那么为什么为5呢?博主带着大家来解析一下。
在上面博主有讲到,按位与操作符同样计算的也是二进制位中的补码,因此首先我们将5和-3的补码表示出来。
-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:00000000000000000000000000000101
将5和-3的补码表示出来后,接下来就要对每一位进行按位与操作,什么叫同时为1则为1呢,譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,由于不同时为1,那么在进行按位与操作后,因此c的二进制位的最高位得到的是0。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1,因此c的二进制位的最低位为1。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是0,因此c是正整数,那么原、反、补码相同,将其二进制转换为十进制得到的就是5。
3.2:按位或操作符(|)
讲完了按位与操作符之后,接下来再讲讲按位或操作符,按位或操作符用一句话总结就是**"其中为1则为1"。**,我们还是来看一段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a | b;printf("%d\n", c);return 0;
}
将5和-3进行按位或操作然后赋值给c,此时c的值为-3,那么为什么为-3呢?博主带着大家来解析一下,同样我们将5和-3的补码表示出来。
-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:11111111111111111111111111111101反码:11111111111111111111111111111100原码:10000000000000000000000000000011
将5和-3的补码表示出来后,接下来就要对每一位进行按位或操作,什么叫其中为1则为1呢?譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,由于存在1,那么在进行按位或操作后,因此c的二进制位的最高位得到的是1。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1,因此c的二进制位的最低位为1。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是1,也就是说此时变量c为负整数,因此我们要求出其原码,补码求出原码的方式:**(1):符号位不变,补码减1得到反码;(2):符号位不变,数值位进行取反得到原码。**得到原码后,将其转换10进制数得到的就是-3啦!
3.3:按位异或操作符(^)
接下来我们再看一个位操作符,按位异或操作符,按位异或操作符用一句话来总结就是"相同为0,相异为1"。我们直接看下面这段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 5;int b = -3;int c = a ^ b;printf("%d\n", c);return 0;
}
将5和-3进行按位异或操作然后赋值给c,此时c的值为5,那么为什么为-8呢?博主带着大家来解析一下。
-3:原码:10000000000000000000000000000011反码:11111111111111111111111111111100补码:111111111111111111111111111111015:补码:00000000000000000000000000000101c:补码:11111111111111111111111111111000反码:11111111111111111111111111110111原码:10000000000000000000000000001000
将5和-3的补码表示出来后,接下来就要对每一位进行按位或操作,什么叫相同为0,相异为1呢?譬如在这里,5的二进制位的最高位为0,-3的二进制为的最高位为1,0和1不是相同的,那么在进行按位异或操作后,因此c的二进制位的最高位得到的是1。再来,5的二进制位的最低位是1,-3的二进制位的最低位也是1;1和1是相同的,因此c的二进制位的最低位为0。其他的二进制位同样按照这种方式进行计算。在进行计算后,得到c的补码,我们发现最高位是1,也就是说此时变量c为负整数,因此我们要求出其原码,得到原码后,将其转换10进制数得到的就是-8啦!
- a ^ a = 0;
- 0 ^ a = a;
PS:对于位操作符,他们的操作数也同样必须为整数。
4.赋值操作符
赋值操作符是一个很棒的操作符,他可以让你得到一个你之前不满意的值,也就是你可以给自己重新赋值。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{//初始化体重为120int weight = 120;//不满意就赋值weight = 90;return 0;
}
4.1:复合赋值符
+=-=*=/=%=>>=<<=&=|=^=
像上面的运算符都可以写成复合的效果,那什么是复合的效果呢,我们来看一段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int a = 10;//等价于 a = a + 1a += 1;int b = 5;//等价于 b = b << 3b <<= 3;printf("%d\n", a);printf("%d\n", b);return 0;
}
这段代码中的a += 1等价于 a = a + 1,用变量a最初值进行加1然后再赋值给a;那么同理,b也是一样,b <<= 3 等价于 b = b << 3;将b左移三位以后然后赋值给b。其他的复合操作符也是同理哦,这里博主就不一一举例啦,uu们下去以后可以自己去进行尝试.
5:单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 求操作数的数据类型长度(以字节为单位)
~ 对一个数的二进制位取反(操作的是补码)
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
5.1:逻辑反操作符(!)
逻辑反操作符是将true(非0值)转换false(0);将false(0)转换为1(非0值)。这句话是什么意思呢?我们看下面这段代码.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int data = 0;int b = !data;printf("%d\n", b);if (b){printf("hello world\n");}return 0;
}
我们定义一个变量data为0,然后对其进行逻辑反操作之后赋值给b,原本data为0,对其进行逻辑反操作后赋值给b,那么此时b为非0值,在之前我们了解过,C语言中非0为真,0为假,因此,此时if的条件判断成立,会在屏幕上打印hello world,同时我们也可以观察b的值是多少。
5.2:正值,负值操作符(+ -)
正值,赋值操作符就是我们在数学中所见到的正负号。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = -5;int b = -a;int c = +a;printf("b = %d,c = %d\n", b, c);return 0;
}
注意:在C语言中对负数使用正值操作符然后再赋值给其他变量,该变量的值依旧为负数。
5.3:取地址操作符和解引用操作符(& *)
取地址操作符与解引用操作符这里博主就直接通过代码来进行讲解。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 10;//定义指针变量p,存储a的地址int* p = &a;//对p进行解引用操作,*p是通过p中存放的地址,找到所指的对象,*p其实就是a*p = 20;printf("a = %d\n", a);printf("*p = %d\n", *p);printf("p =%p\n", p);return 0;
}
这段代码我们定义一个变量a,然后使用&(取地址操作符)取出a的地址赋值给p,在C语言初识时,博主讲过,地址存放指针里头,因此在这里定义了一个指针变量p来存放变量a的地址;然后对指针变量p使用*(解引用操作符)找到其所指的对象,因此 *p 为变量 a
5.4 sizeof操作符
sizeof操作符在之前我们就已经见过了,可以求出变量(数据类型)所占的空间大小。我们直接来看下面这段代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{/*sizeof是计算类型创建变量或者变量的大小,单位为字节sizeof计算的结果是size_t类型的size_t是无符号整型的对size_t类型的数据进行打印,可以使用%zd*/int a = 10;printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(int));printf("%zd\n", sizeof a);return 0;
}
sizeof后面的括号在括号中写的不是数据类型的时候,括号可以省略,因此sizeof不是函数sizeof是操作符,单目操作符。
5.5:按位取反操作符(操作的是补码)
什么是按位取反操作符呢?所谓按位取反操作符,就是将一个数的二进制位补码进行取反,对于0取反之后则为1,对于1取反之后则为0,我们来看下面这段代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 0;int b = ~a;printf("%d\n", b);return 0;
}
0:补码:00000000000000000000000000000000
b:补码:11111111111111111111111111111111反码:11111111111111111111111111111110原码:10000000000000000000000000000001
为什么结果是对0按位取反后结果是-1呢?首先,将0的补码表示出来,然后对其进行按位取反,得到b的补码,我们观察b的补码发现,最高位为1,因此b为负数,然后我们求出其原码,将其转换为10进制以后就能得到b的值为-1了.
5.6:++,–操作符
++与--操作符,这里我们直接看代码。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 5;//先使用,后+1int b = ++a;//a = 6;b = 6 printf("前置++后:a = %d,b = %d\n", a,b);b = a++;//b = 6,a = 7printf("后置++后:a = %d,b = %d\n",a,b);return 0;
}
前置++的窍门:先+1后使用,在这里,先对a进行+1然后再将其赋值给b,那么此时a的值为6,b的值也为6。
后置++的窍门:先使用再+1,在这里,先将a的值赋值给b,由于前面对a使用一次前置++,使用完了以后a再+1,此时a的值为7,b的值依旧为6
PS:前置++和后置++对要自增的变量的本身没区别
5.7:强制类型转换操作符
所谓强制类型转换,就是将一种数据类型强制转换另外一种数据类型.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{//强制类型转换为整型;int a = (int)3.14;printf("%d",a)return 0;
}
在这里,我们将a这个double类型的变量使用强制类型转换,那么此时在屏幕上打印的时候,只会打印整数,不遵循四舍五入哦!只保留整数部分。
PS:在C语言中,直接写出浮点数,会被编译器识别为double类型,例如17.0就会被编译器识别为double类型,如果要令其为float类型,则应表示为17.0f。
6:关系操作符
>
>=
<
<=
!= 用于测试“不相等”
== 用于测试“相等”
这些关系运算符比较简单,博主就不具体讲解啦,但是要注意一些运算符使用时候的陷阱。
PS:在编程的过程中要注意== 和 =不小心写错而导致的错误哦!
7:逻辑操作符
&& 逻辑与
|| 逻辑或
逻辑操作符呢博主在C语言初识那篇博客中详细讲解了概念,这里博主就不再讲解啦,忘记了的uu们可以返回C语言初识那篇博客去看看哦!博主这里就针对这两个操作符的特性来讲解短路操作.我们看下面这两段代码
7.1:逻辑与操作(&&)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>int main()
{int i = 0,a = 0, b = 2, c = 3, d = 4;i = a++ && ++b && d++;printf("a = %d,b = %d, c = %d, d = %d\n", a, b, c, d);printf("i = %d\n", i);return 0;
}
为什么这段代码执行出来只有a的值发生变化了呢?原因在于逻辑与操作符有短路操作的特性,我们在之前讲到过,**逻辑与操作是同时为真则为真,由于对变量a使用的是后置++,那么是先使用 a == 0进行判断,然而这个时候已经出现了0,那么这个表达式则为假,因此就不会再对后面的值进行判断了,这就是短路操作!**由于整个表达式的值为假,因此i的值为0,a的值为1,其他变量的值保持不变。
7.2:逻辑或操作(| |)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int i = 0, j, a = 0, b = 2, c = 3, d = 4;j = a++ || ++b || d++;printf("a = %d,b = %d,c = %d,d = %d\n", a, b, c, d);printf("j = %d\n", j);return 0;
}
为什么这段代码执行出来a,b,j的值发生变化了呢?原因在于逻辑或操作符也有短路操作的特性,我们在之前讲到过,逻辑或操作是其中为真则为真,由于对变量a使用的是后置++,那么是先使用 a == 0进行判断,此时出现了一个0,因此继续向后判断,对b是使用的是前置++,因此是先使用再++,此时b的值为3,在之前我们讲到过,C语言中,0为假,非0为真,因此由于出现了一个真,那么整个表达式的值为真,就不会再继续向后判断,执行后面的语句了。由于整个表达式的值为真,因此j的值为1,a的值为1,b的值为3,其他变量的值保持不变。
8.条件操作符(三目操作符)
所谓三目操作符,其实和之前所学习到了if else语句一样,只是换汤不换药,万变不离其宗,我们直接看下面这段代码.
表达式1的结果为真,则执行表达式2;且表达式2的结果是整个表达式的结果;表达式3不计算
如果表达式1的结果为假,表达式2不计算;则执行表达式3;且表达式3的结果为整个表达式的结果
exp1 ? exp2 : exp3
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int a = 0;int b = 0;int max = 0;scanf("%d %d", &a, &b);max = a > b ? a : b;printf("max = %d\n", max);return 0;
}
我在这里用三目操作符来实现两个数找最大值,若a > b 这个表达式成立的话,那么则将a赋值给max,若a > b这个表达式为假,则将b赋值给max.
9:下标引用、函数调用和结构体成员访问操作符
这三个操作符,博主之前在C语言初识那篇博客中详细讲解了,忘了的uu可以回去看看哦.
9.1:下标引用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//[]下标引用操作符printf("%d", arr[0]);return 0;
}
9.2:函数调用操作符
()函数调用
接受一个或者多个操作数:第一个操作数为函数名,剩余的操作数就是传递给函数的参数。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Product(int x ,int y)
{return x * y;
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//()函数调用操作符printf("hello world\n");int a = 0;int b = 0;scanf("%d %d", &a, &b);//()函数调用操作符int value = Product(a, b);printf("%d\n", value);return 0;
}
9.3:结构体成员访问
. 结构体变量.成员名
-> 结构体指针变量->成员名
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct book
{char name[20];int price;
};
void Print(struct book * b)
{//通过*解引用操作,找到所指的对象printf("%s %d\n", (*b).name, (*b).price);//结构体指针变量->成员名printf("%s %d\n", b->name, b->price);
}
int main()
{struct book b = { "C++书籍",25 };//结构体.成员,找到所指的对象printf("%s %d\n", b.name, b.price);Print(&b);return 0;
}
10:操作符的属性
C语言操作符有三个重要的属性影响因素
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
10.1优先级
优先级指的是,如果一个表达式包含多个运算符,哪个运算符应该优先执行。各种运算符的优先级是不一样的。
3 + 4 * 5;
上⾯⽰例中,表达式 3 + 4 * 5 ⾥⾯既有加法运算符( + ),⼜有乘法运算符( * )。由于乘法的优先级⾼于加法,所以会先计算 4 * 5 ,⽽不是先计算 3 + 4 。
10.2结合性
如果两个运算符优先级相同,优先级没办法确定先计算哪个了,这时候就看结合性了,则根据运算符是左结合,还是右结合,决定执⾏顺序。大部分运算符是左结合(从左到右执行),少数运算符是右结合(从右到左执行),比如赋值运算符
5 * 6 / 2;
上面示例中,*和/的优先级相同,它们都是左结合运算符,所以从左到右执行,先计算 5 * 6,再计算6 / 2。
上图是C语言操作符的优先级顺序,uu们不需要全部记忆,只需要记住大概就好,在使用的时候如果忘记了查看表格即可!
11:表达式求值
11.1:整型提升
C语言中整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整型操作符在使用之前被转换为普通整型,这种转换称为整型提升。
11.1.1整型提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器的操作数的字节长度一般就是Int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。
通用CPU是难以直接实现两个8byte直接相加运算(虽然机器指令中可能存在这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
11.1.2:如何进行整型提升
1:有符号整数提升是按照变量的数据类型的符号位来进行提升的.
2:无符号数整型提升,高位补0.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{char a = 5;char b = 126;char c = a + b;printf("%d\n", c);return 0;
}
5:补码:00000000 00000000 00000000 00000101
a:补码:00000101
126:补码:00000000 00000000 00000000 01111110
b:补码:01111110
c:补码:10000011
整型提升后
c:补码:11111111 11111111 11111111 10000011反码:11111111 11111111 11111111 10000010原码:10000000 00000000 00000000 01111101
上述代码中,将5存放在char类型的变量a中,将126存放在char类型的变量b中,由于char类型只占1个字节即8个bit位,int类型占个4个字节,32个bit位,因此会发生截断,取其前8位存放;那么分别将5和126的补码表示出来,表示出来后,取前8位,分别得到变量a与变量b的补码,最后将其补码相加得到变量c的补码,在打印变量c时,使用的是%d,十进制整数占位符,因此会发生整型提升,那么怎么提升呢?我们已经得到了变量c的补码,char类型的数据是signed char 还是 unsigned char是不确定的,C语言没有明确指定,是取决于编译器的,博主当前使用的VS上,char 类型默认是signed char即有符号数,因此在整型提升时,按照其符号位来进行提升,由于C的符号位为1,因此在整型提升时,进行高位补1,补齐剩下的24个字节,在进行整型提升后,我们得到了补码,由于在打印时打印的是原码,因此我们求出其原码,最后将其转换为10进制的数就能得出c的值为-125。
11.2:算术转换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换
如果某个操作数的类型在上⾯这个列表中排名靠后,那么⾸先要转换为另外⼀个操作数的类型后执⾏运算。
11.3:问题表达式解析
11.3.1:表达式1
a * b + c * d + e * f;
上述表达式在计算的时候,由于*比+的优先级高,只能保证,的计算比+早,但是优先级并不能觉得第三个比第一个+早执行。那么就会如下两种情况的计算顺序。
11.3.2:表达式2
c + --c;
操作符的优先级只能决定自减–的运算在+的运算之前,但是我们并没有办法得知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的。
11.3.3:表达式3
#include <stdio.h>
int fun()
{static int count = 1;return ++count;
}int main()
{int answer;answer = fun() - fun() * fun();printf("%d\n",answer);return 0;
}
上述代码虽然在大多数编译器上求得的结果是一样的,但是在表达式answer = fun() - fun() * fun()
中我们只能通过操作符的优先级得知:先算乘法,再算减法,函数的调用顺序无法通过操作符的优先级确定。
好啦,家人们,关于这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴的小手给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!