1.整形
1.1整形的三种编码方式,原码,反码,补码 plus无符号整形的编码
在c语言中,原码即为该数字的二进制形式,且最高位为符号位。负数的反码是原码的按位取反的结果。负数的补码则是反码+1的结果。正整形的原反补都是相同的
无符号整形在内存中无符号位这一说法,就像是数学中的绝对值,最高位不再决定符号,而是参加值大小的决定。
举例:
int a = 1;//int 类型在内存中占据四个字节,共有4*8个二进制位,且最高位为符号位
//a的原码:00000000 00000000 00000000 00000001
//a的反码:00000000 00000000 00000000 00000001
//a的补码:00000000 00000000 00000000 00000001
//可知正数的原码反码补码是相同的
//(注意,最高位为符号位,符号位为1时数字为负数,为0时为正数)
int b = -1;
//b的原码:10000000 00000000 00000000 00000001
//b的反码:11111111 11111111 11111111 11111110 (除去符号位外按位取反即为负数的反码)
//b的补码:11111111 11111111 11111111 11111111 (在反码基础上+1)
unsigned int c = 1;
//其在计算机中的存储方式与正整形相同:
//00000000 00000000 00000000 00000001
1.2补码的意义以及如何转换为十进制
在计算机中整形数值的运算和存储都是通过补码(无符号整型和正整形补码和原码相同)进行的,通过补码运算我们可以将加法和减法合并为加法。
举例
int a = 1;//补码为 00000000 00000000 00000000 00000001
int b = -1;//补码为11111111 11111111 11111111 11111111
int c = a + b;//a + b 等同于 1 - 1 上面二值相加结果为 00000000 00000000 00000000 00000000
printf("%d",c);//打印出0
补码转化十进制公式:
若为n位二进制数为abcdfghijkl.........z(各位取值只有01)其十进制表现形式为:
得到的轮回结论
由此公式我们可知,在计算机的存储中若一味地给变量增加数值,在成为正极限数值(受限于数据类型的字节大小)后又会变成负极限值,即只有a为1,其余都为0的情况,最后又随着给变量增加数值,数值又会慢慢回到-1,即全1的情况,再加1又会变成零,继续给变量增加数值又会变成最大正极限数值,即一次轮回。如图所示:
2.浮点型
2.1浮点型如何转化为二进制
浮点型数据转化为二进制,其整数部分与前面的整形转化是相同的,不同之处在于小数点后面的那个部分,其实也很简单:将小数部分*2,第一次乘若得到1则在第一位位写上1,并去掉一继续下一次乘2,若得到0则在第一位写上0,并且继续下一次乘法。重复以上操作直到小数部分全为零为止。(注:有时会有小数部分乘不尽的情况,根据情况进行舍去相应位数即可)
2.2浮点型在二进制中如何存储
C语言的浮点数是遵循IEEE(Institute of Electrical and Electronics Engineers)电气电子工程师学会的754标准。其标准是将二进制转化为如下形式:
第一部分s为最高位,即符号位,第二部分M为有效数字部分即1.1101101(举例),第三部分即为阶码部分。
而在电脑中,计算机存储的方式由高到底分别为SEM,如图:
其中S决定符号,只占一位,E指数部分则需要加上偏移量(float为127 double为1023) ,在float中占八个bit double中占11个bit。M则为有效数字部分去掉整数的部分,按照上面给出的有效数字举例,其储存则为“1101101后面加上若干个零”。在float中M占23个bit,在double中M占52个bit。
下面给出float类型11.101如何存储的示例
3.大小端存储
1.概念
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
来源:百度百科.大小端模式
来自本人的记忆的小技巧:在现实世界中,高位是一直延伸向无穷的,所以我们可以认为高位并不是一个数的端点,而低位一直是在小数点之前或者根本没有小数点,也就是永远的第零位。那么我们就可以认为第零位即低位为这一串数字的“端”。而大端就是把低字节放在较高地址位上,地址数值大,放着所谓的“端” 即为大端,小端也就同样能得出了。
2.判断大小端的代码
主要思路是利用char* 访问一个字节内容
void judge_system()
{
int a = 1;
char* lookin = (char *) & a;
if (*lookin)
{
printf("小端");
}
else
{
printf("大端");
}
}
最后再看一个“奇怪”的代码
void strange(void)
{
int i = 0;
int a[10] = {0};
for(i = 0; i<50; i++)
{
printf("hello");
a[i] = 0;
}
}
这个代码再vs2022 x86 debug的版本下运行会陷入死循环。可是这是为什么呢,明明只是让他进行五十次循环呀?
如果陷入死循环,那必然有他的道理。
在堆栈的过程中,c语言通常是从高往低去申请地址来用的,就会形成如下图的情况
随着i的增加,慢慢地又会越界访问到上面的地址,上面的i又会被改成0,最后导致了死循环。
但是在release版本下就不会出现这种情况,说明在release版本下编译器也会对代码的解读作出相应优化