初学C语言:整型提升引发的思考

发布于:2023-01-14 ⋅ 阅读:(281) ⋅ 点赞:(0)

前言:本人为C语言初学者,学识尚浅,研究程度存在很大的局限性,眼界很窄。以下所有观点仅代表个人见解和思路,各位游刃有余的前辈可以给予批评和指正!各位与鄙人同路的学子可相互探讨、发表看法,交换观点!

整型提升的概念是:

表达式的整型运算一般在CPU内的整型运算器(ALU)中执行,而其中的操作数的字节长度一般是int的字节长度,同时也是CPU的通用寄存器的长度,故如果两个空间不满int类型的整数相加,就会发生整型提升,即先转换成int或unsigned int类型再计算

转换方式:

有符号则以符号位填充,无符号则填充0

注:如果还不太清楚,可以先看看初学C语言:运算符,一个也不放过的最后关于整型提升的解释。

例1:

char a = -128;
printf("%d ", a);
printf("%u ", a);

这里需要知道的是,char在C语言标准中没有规定默认是 signed char 还是 unsigned char,是由编译器决定的,在VS里,默认为signed char。

总所周知,char类型的范围是 -128 ~127,-128是整型,赋值给字符型的a,不是仅仅赋值这么简单,其实过程是这样的:

-128 -- 11111111 11111111 11111111 10000000

截断,a = 10000000

a为signed,a = -128

%d输出的是有符号整型,a转换成了 11111111 11111111 11111111 10000000 输出

首位的1是符号位,这里是补码,如果我们想转换成原码(补码 - 1后按位取反(符号位不变)):

补码 - 1:11111111 11111111 11111111 01111111

除符号位按位取反:00000000 00000000 00000000 10000000 = -128 

而%u是无符号整型输出,那么结果不可能是负数呀?

-128 --11111111 11111111 11111111 10000000

截断,a = 10000000

a为signed,a = 128

%u输出的是无符号整型,a是有符号字符型,a输出时发生整型提升,而填充时看的是a的符号位,a不是无符号型,所以不会填充0,而是会填充1

a转换成了 11111111 11111111 11111111 10000000

但是这里首位的1不会被当作符号位,所以输出的是 4294967168

 

例2:

char a = 128;
printf("%d ", a);
printf("%u ", a);

例1中我们提到,signed char 类型为 -128~127,那么我们在a中放128,岂不是比127要大吗?

照旧,128 = 00000000 00000000 00000000 10000000

截断,a = 10000000

而a是有符号字符型,1被解读成符号位,且存在内存中的为补码形式,而这里需要着重强调的是,10000000不能够 -1 再按位取反,怎么说也不可能把符号位减没了吧,那么这串数字到底表示什么?-1是 11111111,-2是11111110......那么-127是10000001,-128不就是10000000嘛?

所以如果你看到 10000000,将其对应成 -128 即可,不必过于纠结,如果你还是纠结,不妨看看下面这个图:

 

这样是不是更形象了~(都是补码形式哦!)

所以以%d打印的时候,结果就是:-128        

而如果以%u打印,以上可得 a = 10000000

但是a是有符号位的字符型,所以在发生整型提升的时候前面填充的应该是符号位,也就是1,填充后为:11111111 11111111 11111111 10000000

%u输出的是无符号整型,所以1不被当作符号位,而是直接转换成十进制数,即 4294967168

 

例3:

int a = -20;
unsigned int b = 10;

printf("%d", a + b);

说白了,这不就是二进制相加嘛,我们分别写出它们在内存中的补码:

a = 11111111   11111111  11111111   11101100 

b = 00000000 00000000 00000000 00001010

a + b = 11111111 11111111 11111111 11110110

然后我们转换成原码看看是啥:

补码 -1:11111111 11111111 11111111 11110101

除符号位按位取反:10000000 00000000 00000000 00001010

%d输出,即首位1为符号位

即 -10

 

乍一想,那不就是 -20 + 10的运算结果嘛,但实际的运算逻辑如上,并不是-20+10那么简单,其实这里还有一点值得探讨,就是有符号整型和无符号整型相加,到底是转换成什么类型相加的呢,换言之,他们的值属性是什么类型?

	int i = -20;
	unsigned int j = 10;
	if (i + j > 0)
	{
		printf("大于0!\n");
	}
	else
	{
		printf("小于0!\n");
	}

运行结果是这样的:

然后,然后你就蒙了......

这不是明摆着小于0?甚至我们刚刚计算的也是 -10

这里就要讨论其值属性了,有符号整型和无符号整型相加,其实是转换成无符号整型的,我们计算的结果之所以是 -10,是因为我们是以%d输出的,即有符号整型输出,所以首位1被解释成了符号位,但是论其值属性,仍是无符号整型,所以在进行判断时,认为其是无符号整型,肯定大于0咯。

例4:

unsigned int i = 1;
for(i = 1; i >= 0; i--)
{
    printf("%u\n", i);
}

i -- 一次为0,如果再减一次呢?

其实我们站在无符号数的角度来讲,i 怎么变都不可能小于0,所以永远满足我们的判断条件,故我们大概能知道它应该是个死循环。

我们调试结果如下:

 可以看到如果把0再减去1,结果变成了ff ff ff ff,换言之,我们如何解释0 - 1?开头我们就提到,计算机中只有加法计算器,所以我们应该翻译成 0 + (-1),所以内存中放的值就是0x00000000和0xffffffff的相加,即0xffffffff,而这个值本应该是-1,但是因为是无符号整型,所以首位不被解释为符号位,即变成了4294967295(FFFFFFFF)。所以每当i的值减到0然后再减去1,就会重新变成ffffffff再继续循环,于是陷入了死循环:

 

例5:

	char a[1000];
	int b = 0;
	for (b = 0; b < 1000; b++)
	{
		a[b] = -1 - b;
	}
	printf("%d\n", strlen(a));

如果要解析这一题,首先我们要知道,这个循环肯定是会停的,因为b是可以大于等于1000的,但是我们打印的是字符串长度,换句话说,我们要知道字符串是什么时候结束的,而字符串的结束标志是 '\0' 所以,如果b填充了一个0进去,这个字符串就被判定结束了。

而char类型只能最多到-128,同理,当-1 -128时会发生什么?老样子,我们看成 (-1) + (-128)

- 1 =     11111111 11111111 11111111 11111111

- 128 = 11111111 11111111 11111111 10000000

即 = 1 01111111 11111111 11111111 01111111

存到a数组中时发生截断,即a[b] = 01111111,变成了127,等再减到0的时候,字符串就标志结束了。

所以结果应该是128 + 127 = 255

这里其实还想提一嘴,比如 -1 + -1,结果都知道是 -2

但是在内存中,他们是这样的:

11111111 11111111 11111111 11111111

+

11111111 11111111 11111111 11111111

=

1 11111111 11111111 11111111 11111110

而最前面的1溢出,会被舍弃

所以我们得到的是:11111111 11111111 11111111 11111110 = -2

例6:

	unsigned char a = 0;
	for (a = 0; a <= 255; a++)
	{
		printf("hello world\n");
	}

我想这应该已经很简单了,unsigned char的取值是 0~255,所以我们要讨论的就是255再加上1会怎样,而255在内存中是:

00000000 00000000 00000000 11111111

截断后在a中就是:

11111111

再加1,在内存中就是:

00000000 00000000 00000001 00000000

截断后在a中就是:

00000000

即回到0重新开始,所以结果肯定是hello world循环。

END

(其实那么多数只是内存中储存的数据罢了,但是比如%d和%u的使用,让FF FF FF FF有不同的解释,这些是编译器做的,比如你用%d,编译器就会把二进制第一位当作符号位;你用%u,编译器就会把二进制第一位当作正常数据)<-这个或许是写给我自己看的!

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

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