从0开始学c语言-19-隐式类型转换(整型提升)、算术转换、操作符属性及应用练习

发布于:2022-12-16 ⋅ 阅读:(709) ⋅ 点赞:(0)

本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。

下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。

 上一篇:从0开始学c语言-18-操作符详解_阿秋的阿秋不是阿秋的博客-CSDN博客

目录

表达式求值

1·隐式类型转换

整型提升的意义:

例子1-怎么整型提升

例子2-数据的储存、运算、输出对比

例子3-和sizeof的结合

2·算术转换

3·操作符的属性

例子1:

例子2:

例子3:非法表达

例子4:

例子5:

总结


建议看之前,把上一篇看了,这俩属于同一范畴的,链接在最上面。

表达式求值

表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型

隐式类型转换

C 的整型算术运算总是至少以整型类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为 整型 提升。

整型提升的意义

表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。

简单点就是说:char和short在运算的时候因为达不到int类型的大小才进行提升的,达到int大小的就不用提升。

例子1-怎么整型提升

int main()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	printf("%d\n", c);
//-126
	return 0;
}
分析一下怎么得到-126的
int main()
{
	char a = 3;
	//00000000 00000000 00000000 00000011 -3
	//char只能存放一个字节也就是八个bit位的二进制序列
	//00000011 - a -截断

这是char a变量的整型提升过程,因为后续要执行和char b变量的相加,所以在使用之前需要进行整型提升,然后再执行加法运算,加法结果截断后存储于char c变量中

-1上面这段整型提升的原理是?

整形提升是按照变量的数据类型的符号位来提升的 ,无符号则高位补0
//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
11111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111

//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001

-2细化例子过程

1-a和b的存放  把3和127的二进制序列存放到char a和b,在存放时候进行截断。

2-a和b的相加  在运算时候进行整型提升后相加,又因为char c只能储存8bit位,所以对相加结果进行截断并存放。

3-打印结果  打印的时候因为c是char 类型,而我们进行的是整型打印,所以需要进行整型提升,在整型提升过程中发现最高位的符号位是1(负数),所以我们需要对补码进行转换,进行原码输出。

int main()
{
//char就意味着是有符号char,最高位是符号位

	char a = 3;
	//00000000 00000000 00000000 00000011 -3
	//char只能存放一个字节也就是八个bit位的二进制序列
	//00000011 - a的存放(补码) -截断

	char b = 127;
	//00000000 00000000 00000000 01111111 -127
	//01111111 - b的存放(补码) -截断
	// 整型提升按照变量数据类型的符号位来提升
	// 无符号高位就补0
	

	char c = a + b;
	//a和b都是char类型的,没有达到int大小
	//就会发生整型提升
	//00000000 00000000 00000000 00000011 -a的整型提升
	//00000000 00000000 00000000 01111111 -b的整型提升
	//00000000 00000000 00000000 10000010 -c=a+b的结果
	//10000010 - c的存放(补码) -截断
	
	printf("%d\n", c);
	//打印是以整型方式打印的,所以c要整型提升
	//11111111 11111111 11111111 10000010 -c(补码)的整型提升
	//11111111 11111111 11111111 10000001 -c的反码
	//10000000 00000000 00000000 01111110 -c的原码
	//-126
	return 0;
}

-3 %u输出结果是?

猜一猜,如果我们换成%u  --- 也就是无符号整型的方式进行打印,结果会是?

提示:整型提升是看变量的数据类型的符号位来提升的 ,无符号则高位补0。

//10000010 - c的存放(补码) -截断
	
	printf("%u\n", c);
	//打印是以无符号整型方式打印的,所以c要整型提升
//整型提升是看变量的数据类型的符号位来提升的 ,无符号则高位补0
//变量c的类型是char,我们默认char是有符号的类型
//那么在提升的时候,按照c变量数据类型的符号位来提升,补1
	//11111111 11111111 11111111 10000010 -c(补码)的整型提升

最后这补码转换成十进制的结果就是输出的结果,不用进行反码和原码的转换,因为我们是以无符号整型的方式进行输出,所以第一个1不是符号位。

也就是说,整型提升的时候要看变量数据类型是否有符号不用看输出形式有无符号,输出形式只影响输出结果,对于整型提升没有作用的。

例子2-数据的储存、运算、输出对比

0x代表后面的数字是16进制数字。

int main()
{
 char a = 0xb6;
 short b = 0xb600;
 int c = 0xb6000000;
 if(a==0xb6)
 printf("a");
 if(b==0xb600)
 printf("b");
 if(c==0xb6000000)
 printf("c");
 return 0; 
}

因为运算的时候,char和short不够int的大小,所以我们对a和b进行整型提升。

char a = 0xb6;
//00000000 00000000 00000000 10110110 -0xb6
//10110110 - a截断并储存

 if(a==0xb6)
 printf("a");
//因为a要和0xb6进行比较,而a是char类型
//所以a要进行整型提升
//10110110 - a
//11111111 11111111 11111111 10110110 -a的整型提升
//00000000 00000000 00000000 10110110 -0xb6
//因此不成立

同理,大家试试自己梳理一下b和c变量的能否进行输出。

这里直接说结果了

输出结果是c。(c不需要进行整型提升,c是int类型。)

这里放一下监视细节,重点观察好变量对应的类型和值。

要特别注意 不同类型所储存的值的字节大小不相同
下面这张图可以看到a和b存放的都是 负数,所以在整型提升的时候不会等于之前储存的值。
那么你就要问了,为什么 c是负数却会输出呢
因为c是int类型啊!比较的时候不需要进行整型提升,他们的补码是相同的,只不过输出形式不同而已。
要特别 注意数据的储存、对比、运算以及输出时候的不同应用

数据在储存的时候,会受到数据类型的影响,进行截断

数据在对比、运算的时候,会受到整型提升的影响(尤其是有符号的数据类型)。

数据在输出的时候,以补码为起点,进行不同形式原码输出

例子3-和sizeof的结合

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

输出结果是1 4 4 。

我们曾在从0开始学c语言-18-操作符详解_阿秋的阿秋不是阿秋的博客-CSDN博客

这篇文章中说 sizeof中的表达式不会参与运算,那么+c和-c应该不会进行整型提升,输出1才对啊
我们用这段代码来看看c是否发生变化了。
int main()
{
	char c = 1;
	printf("%u\n\n", sizeof(c));//sizeof - %u无符号整型

	printf("%u\n", sizeof(+c));
	printf("%d\n\n", c);

	printf("%u\n", sizeof(-c));
	printf("%d\n\n", c);
	return 0;
}

 可以看到,-c后并没有输出-1。

可见sizeof中的表达式确实没有进行运算,那么+c、-c为什么size会变为4呢?

sizeof中的表达式不会影响c本身,c不会变化,只是sizeof输出的结果是运算后的
一个数据有值属性和类型属性,sizeof中的表达式不参与运算,但是它打印的是这个表达式如果参与运算之后产生的类型大小。

算术转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转换
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
但是算术转换要合理,要不然会有一些潜在的问题。比如:
float f = 3.14;
int num = f;//隐式转换,会有精度丢失

操作符的属性

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。
两个相邻的操作符先执行哪个?取决于他们的优先级,但是 优先级不能控制求值顺序 。如果两者的优先级相同,取决于他们的结合性。(同一优先级的运算符,运算次序由结合方向所决定。

运算符优先级_百度百科

简单一些记:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符

例子1:

a*b + c*d + e*f
在计算的时候,由于 * + 的优先级高, 只能保证*的计算比+早 ,但是优先级并 不能决定第三个*比第一个+早执行。

也就是说会有两种运算顺序,先算加法的两个操作符

a*b 
c*d 
a*b + c*d 
e*f 
a*b + c*d + e*f

或者先算所有乘法

a*b 
c*d 
e*f 
a*b + c*d 
a*b + c*d + e*f

例子2:

c + --c;
操作符的优先级 只能决定自减--的运算在+的运算的前面 ,但是我们并没有办法得 知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义的。

例子3:非法表达

int main()
{
 int i = 10;
 i = i-- - --i * ( i = -3 ) * i++ + ++i;
 printf("i = %d\n", i);
 return 0; 
}
在不同编译器中测试结果都不同。

例子4:

int fun()
{
     static int count = 1;
     return ++count; }
int main()
{
     int answer;
     answer = fun() - fun() * fun();
     printf( "%d\n", answer);//输出多少?
     return 0; }
我们只能通过操作符的优先级得知:先算乘法, 再算减法。 函数的调用先后顺序无法通过操作符的优先级确定

例子5:

int main()
{
 int i = 1;
 int ret = (++i) + (++i) + (++i);
 printf("%d\n", ret);
 printf("%d\n", i);
 return 0; }
这段代码中的第一个 + 在执行的时候,第三个 ++ 是否执行,这个是不确定的,因为依靠操作符的优先级和结合性是无法决定第一个 + 和第三个前置 ++ 的先后顺序。

总结

我们写出的表达式如果 不能通过操作符的属性确定唯一的计算路径 ,那这个表达式就是存在问题
的。


网站公告

今日签到

点亮在社区的每一天
去签到