整型提升
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int类型字节的长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转化为CPU内整型操作数的标准长度。通用CPU(general - purpose CPU) 是难以直接实现两个8比特字节直接相加的(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int类型长度的整型值,都必须先转化为int型或者unsigned int ,然后才能送入CPU去执行运算。
-
二进制表示的有: 原码,反码,补码;
存储到内存的为补码:
打印到屏幕上的为源码: -
signed char (有符号的字符型) -128 - 127
unsigned char (无符号的字符型) 0 - 255short 占 2个字节 -32768,32767
如何进行整型提升?
#include<stdio.h>
int main()
{
char a = 3;
// 00000011
// 整型提升后 00000000 00000000 00000000 00000011
char b = 127;
// 01111111
// 整型提升后 00000000 00000000 00000000 01111111
char c = a + b;
// 相加后可得 00000000 00000000 00000000 10000010
// 由于c 是char 类型的所以只能存储一个字节,即在c中存储的为 10000010
// 因为要在要打印整型,所以10000010要整型提升
//提升后的结果11111111 11111111 11111111 10000010---为补码
// 化为原码为 10000000 00000000 00000000 01111110
printf("%d",c); //因此可得 -126
return 0;
}
#include<stdio.h>
int main()
{
char a = 0xb6;
char d = 0x78;// 十进制数为120
//0xb6 = 182 超过了 char 范围
short b = 0xb600;
//0xb600 = 46592 远远超过了 short的范围
// 当超过范围的大小的时候整型提升也就不会等于那个数了
int c = 0xb600000;
if(a == 0xb6)
printf("a");
if(b == 0xb600)
printf("b");
if(c == 0xb600000)
printf("c\n");//------打印c
if(d == 0x78)
printf("d");// -----打印d
return 0;
}
整型截断
当把一个整数存入char或者short类型的变量时,会发生截断,char取1个字节,short取两个字节。
接下来看了几个例子:
1.
#include<stdio.h>
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);//a = -1,b = -1, c = 255
return 0;
}
- a是一个char类型的变量,-1是一个整型常量,把-1赋值给a时,会发生截断。-1在内存中的补码为11111111 11111111 11111111 11111111,截取低八位11111111赋值给a,此时变量a中存储的补码为11111111。
当以%d的形式打印变量a的时候,会发生整型提升。由于a是signed(即有符号)类型,==因此向前补最高位符号位的数字1,提升后的补码为11111111 11111111 11111111 11111111。由于%d是有符号的,因此最高位解读为符号位,结果就是-1。
- b是signed char类型的,原理同a。
- c是一个unsigned char类型的变量,-1是一个整型常量,把-1赋值给c时,会发生截断。-1在内存中的补码为11111111 11111111 11111111 11111111,截取低八位11111111赋值给c,此时变量c中存储的补码为11111111。
当以%d的形式打印变量c的时候,会发生整型提升。由于c是unsigned(即无符号)类型,因此向前补数字0,提升后的补码为00000000 00000000 00000000 11111111。由于%d是有符号的,因此最高位为解读为符号位,结果就是255。
2.
#include <stdio.h>
int main()
{
char a = -128;
char b = 128;
printf("%u\n", a); // ----4294967168
printf("%u\n", b); // ----4294967168
return 0;
}
128和 -128都是整型常量,赋值给char类型的变量时会发生截断,128的补码为0000000 00000000 00000000 10000000,-128的补码为11111111 11111111 11111111 10000000,截断后a,b内存储的都为10000000。
a,b以%u打印的时候会发生整型提升,由于a,b都是有符号的char类型,因此向前补最高位符号位1,提升后的补码为11111111 11111111 11111111 10000000。由于%u打印的是无符号整型,此时最高位被视作数值位,因此原码与补码相同,该原码转化成十进制就是4294967168。
3.
#include<stdio.h>
int main()
{
unsigned int i = 0;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
结果死循环 —>原因如下:当i==0进入的时候,打印0,然后i–,i看似变成了-1,此时内存中存储的补码为11111111 11111111 11111111 11111111。但由于i是一个unsigned int类型的变量,因此最高位应当看作数值位,此时原码和补码相同,而该原码代表的数值为4294967295,因此i会从4294967295开始进入循环,而当i为0的时候,又会重复上述过程,导致死循环的产生。
#include<stdio.h>
#include<string.h>
int main()
{
char arr[1000] = { 0 };
int i = 0;
for(i = 0;i < 1000;i++)
{
arr[i] = -1 - i;
}
printf("%d",strlen(arr));//255
return 0;
}
有符号的char类型有下面的运算图, - 128 ~ 127
由程序可得arr[]数组里的元素为下:
arr[i]= {-1,-2,-3,...... -128,127,126,....2,1,0......}
可得在arr[i] = 0的时候即ASCII码值为即为‘\0’,此时strlen停止计算字符长度。
#include<stdio.h>
unsigned char i = 0;
int main()
{
for(i = 0;i <= 255;i++),
{
printf("Hello world\n");// hello world 死循环
}
return 0;
}
和上面的题目一样,unsigned char 范围是0 ~ 255,当i = 0的时候,i++,二进制为:
1 00000000 00000000 00000000 00000000,截取后仍为0,将陷入死循环。
总结
整型提升可以分两部分来理解(以char类型为例):
1、看该char类型的变量是signed还是unsigned类型。signed类型在整型提升时向前补最高位符号位,unsigned类型在整型提升时向前补0。
2、看是以%d形式还是以%u形式输出。%d形式代表以有符号视角来解读,因此最高位视为符号位,%u形式代表以无符号视角来解读,因此最高位视为数值位