在上一篇文章中,我们学习了整型和浮点型在内存中的存储,但是有些内容讲解的比较模糊,所以在这篇文章中,将会进行针对性的讲解与练习
1.🚩整型提升
简单来说,占用字节数次小于int的,在使用之前会被提升为int(四个字节),这个过程就是整型提升。
至于为什么是整型提升,有没有短整型提升?这主要是计算机体系结构来决定的,计算机进行计算时,产生作用的是CPU中的ALU(运算器),而ALU的操作字节最小就是int的字节数,而数据实际上是存在寄存器中(register),寄存器的字节大小与ALU操作字节数保持一致,也是四个字节,所以会发生整型提升。
整型提升遵循两个原则
1.如果是无符号数,在最高位补0。 |
2.如果是有符号数,在最高位补符号位。 |
我们举个例子来看一下:
我们把0xbb赋给了a,然后让a与0xbb比较,看看结果如何。
在这里解释一下0xbb是十六进制,对应的十进制是11*16+11=167,对应的二进制是 1011 1011
在上一篇博客中我们说,计算机中存储数据都是用补码表示,我们看到的数据都是原码,在这里, 我们把 1011 1011赋给了char a,相当于a现在在内存中存储的是 1011 1011,那1011 1011就是补码,我们把他转化成原码就是
1100 0101 因为char是有符号数,那就是-69。
我们再看a==0xbb这个表达式,0xbb 是一个整型常量 也就是
0000 0000 0000 0000 0000 0000 1011 1011
再看a,此时a是char,需要进行整型提升,因为是有符号数,所以补上符号位,也就是全部1,此时a为
1111 1111 1111 1111 1111 1111 1011 1011
显而易见a与0xbb不相等,在进行运算以后,a又会发生截断,取低八位
又变成了 1011 1011
这就是一个整型提升的过程,在这里我们又提出了一个概念,就是截断,那什么是截断呢?
🚩截断
当变量接收了一个超过他所有字节数的变量时,就会发生截断,保存所能容纳的位数,其余高位抛弃:
例如 char a = 4294967295;
,4294967295是2^32-1 ,对应的二进制存储就是
1111 1111 1111 1111 1111 1111 1111 1111 那么存储到a里的会是什么呢?
取低八位 1111 1111 ,并转化成原码 1000 0001,那么a == -1;
这就是发生了截断。
了解了基本概念之后我们练习几道题,来看看我们理解的怎么样:
🚩习题
📍例题一
int main()
{
char a = -1;
signed char b = -1;
unsigned char c = -1;
printf("a=%d,b=%d,c=%d", a, b, c);
return 0;
}
让我们分析一下会打印出什么?
首先把 -1存入了a ,-1的原码是 1000 0001 补码就是 1111 1111
char 是有符号的,所以a和b,打印出来都是-1,但是看c,c是无符号数,他直接把补码看成原码,直接翻译过来就是 27-1 =255.
所以打印结果是 -1 -1 255
📍例题二 知识点(整型提升,截断)
#include<stdio.h>
int main()
{
char a = -128;
printf("%u\n", a);
return 0;
}
我们分析一下,用无符号整型打印a,会打印出什么?
首先-128是一个整型,把他赋给a,是要发生整型提升的,也就是原来-128的原码是 1000 0000 补码是 1000 0000 在整型提升以后,char是有符号的,提升的是符号位,-128提升为了 1111 1111 1111 1111 1111 1111 1000 0000,但是我们知道,存入char类型中只能存入两个字节的数据,也就是八个比特位,那么除了低八位,要发生截断,最后留在a中的数据就是 1000 0000
可是当你在用无符号打印的时候,由于运算器只能从四个字节开始操作,你又要进行整型提升,char是有符号数,所以又要补上符号位,也就是
1111 1111 1111 1111 1111 1111 1000 0000,这是一个很大的数,可以放在计算器中我们看一下他是多少
那我们在打印一下,看看程序的结果到底是多少:
可以看到,与我们计算的结果一致,考虑到这块有一点绕,我们再进行一个练习
📍例题三
#include<stdio.h>
int main()
{
char a = 128;
printf("%u\n", a);
return 0;
}
这时,打印出的数据又会是多少呢?这个大家可以自己练习一下。
📍例题四
int main()
{
int i = -20;
unsigned int j = 10;
printf("%u\n", i+j);
return 0;
}
我们来分析一下这个问题, -20的补码是:
1111 1111 1111 1111 1111 1111 1110 1100
10的补码是:
0000 0000 0000 0000 0000 0000 0000 1010
计算机中加法是用补码和补码相加:
1111 1111 1111 1111 1111 1111 1111 0110
最后用无符号整数打印出来,因为是无符号数,原码反码补码相同,所以打印的就是 补码的十进制值:
♨️♨️♨️例题五
#include<stdio.h>
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
分析一下,这个循环是正确的吗,它会什么时候终止呢?
经过上面的练习相比你已经有了些许感悟,这是一个死循环,为什么呢?
因为i是一个无符号整数,当i减到-1时,经过编译 -1的补码是 1111 1111 1111 1111 1111 1111 1111 1110 ,而对于i来说,i是一个无符号的整数,原码反码补码都相同,所以他是一个很大的整数,最后演变成一个死循环
♨️♨️♨️例题六 知识点(循环)
#include<stdio.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; ++i)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
请问,数组的长度会是多少?
我们都知道strlen遇到’\0’会停止技术,那么这道题的关键就是找到’\0’,但是仔细看这题,一直是-1-i,只会越来越小,也不会到0啊,这时候就用到了一个循环的概念:整型变量中的数据是在其范围中一直循环的例如:
char类型数据的范围是-128~127,那么我给他一个128,它实际上打印出来的数据会是多少呢?
那129呢?
这足够说明,数据是一直循环的,我们画图理解一下:
有点简陋,见谅,主要体现的是一个循环的思想。也就是说我们在不断增加的过程中,数据是循环的,同理,int long 这些数据类型也是循环的,只不过这个圈要更大一些。
那么回到题中,-1要减多少次才会遇见0呢,看图我们可以知道,从-1到-128要减127次,从-128到0又要减128次,加起来就是255次,所以长度应当是255
小结
要补充的东西就这么多,比较重要的知识点就是整型提升,截断,以及循环的概念,希望大家能多加练习,也请看我博客的同学们给予一些意见,指出一些错误,谢谢大家。