🌻大小端字节序
数据存储在内存中,内存中的地址有高低地址之分,起始地址即低地址。
数据是以二进制形式存储的,二进制也有高低位之分,最左边是高位,左边是低位。
因此,数据存储时是从二进制的低位开始存储还是高位呢?
🌸取决于数据是由谁处理的——CPU架构。
常见的CPU架构:X86架构->x86_64架构(向前兼容x86)。
大端字节序:低地址存高位(X86架构)。
小端字节序:低地址存低位(MIPS)。
字节序:CPU对内存中数据进行存储的顺序(不同的CPU可能会有不同的字节序)。
这就导致:不同的机器对相同数据进行解释时存在差异(二义性)。
字节序问题主要影响存储单元大于一字节数据类型的数据(short、int、long、float、double)。因为字节序主要就是以字节为单位进行存储时顺序的不同,如果一个数据只占一个字节,酒不涉及字节序的问题。
如何判断一台主机的字节序?
int a=1;//0x 00 00 00 01
对于整型变量a,如果它的起始地址是1,则主机是小端,否则是大端。
在一个整形的空间中,只取出起始地址处的一个字节数据:
char *b=(char*)&a;
完整代码:
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = 1;
char *b = (char*)&a;
if (*b == 1){
printf("小端字节序\n");
}
else{
printf("大端字节序\n");
}
system("pause");
return 0;
}
⭐在网络通信时,当通信双方字节序不同时,使用网络字节序的标准——大端字节序。
🌼基础数据类型:
整形:char、short、int、long
unsigned char、unsigned short、unsigned int、unsigned long
浮点型:float、double
🌻整形的存储
有符号数据的存储:符号位(1表示负数,0表示正数)和数值位。
无符号数据的存储:都是数值位。
例如,char类型占一个字节8个比特位,最高位是符号位,低7位是数值位。
整形的存储有三种方式:原码、反码、补码,实际存储使用补码。
原码:二进制的直接解释。 -1---1000 0001 1---0000 0001
反码:对原码符号位不变,数值位取反。-1---1111 1110 1---0000 0001
补码:反码+1. -1---1111 1111 1---0000 0001
正数的原码反码补码都是一样的,与原码一致。
为什么要用补码进行存储呢?
🌸使减法可以使用加法器进行计算。
#include<stdio.h>
#include<stdlib.h>
int main(){
//char a = 128;//0000 0000 1000 0000 截断低8位进行赋值 1000 0000
char a = -128;//1111 1111 1000 0000 截断低8位进行赋值 1000 0000
printf("%u\n", a);//%u表示以无符号整形进行解释——a会进行整形提升高位补1,0x FF FF FF 80
system("pause");
return 0;
}
运行结果:4294967168
![](https://i-blog.csdnimg.cn/blog_migrate/67814ed57fd993e377024dcc830a25df.png)
有符号数据整形提升时,以符号位的值进行补位。
无符号数据整形提升时,以0补位(不会影响数值解释)。
#include<stdio.h>
#include<stdlib.h>
int main(){
char a = 128;//1000 0000
printf("%d\n", a);//%d表示十进制打印
system("pause");
return 0;
}
运行结果:-128
🌸128超出了char所表示的范围(-128~127),因此会给变量a一个更大的空间来存储,低位截断后仍然是1000 0000,即-128。
#include<stdio.h>
#include<stdlib.h>
int main(){
unsigned int i;
for (i = 9; i >= 0; i--){
printf("%u\n", i);
}
system("pause");
return 0;
}
运行结果:无限循环
🌸无符号数据不会出现负数,当i是0时,i--为-1,以无符号解释0xff ff ff ff是一个很大的数字,下次减到0的时候再进行下一次循环。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
char a[1000];
int i;
for (i = 0; i < 1000; i++){
a[i] = -1 - i;//a[i]是char类型,将-1-i的结果截取低8位进行赋值,当结果为-256时,恰好低8位是0
}
printf("%d", strlen(a));//strlen是求字符串的长度,其实就是看a数组哪个数据为0,数组中存储的为-1~-255,-256是结尾标志。
system("pause");
return 0;
}
运行结果:255
-256
10000001 00000000原码
11111110 11111111反码
11111111 00000000补码
🌻浮点型的存储
小数类型为什么叫浮点型?
🌸小数点位置是浮动的。
浮点型的存储——IEEE754标准文档规定。
例如,float占4字节,32个比特位,这32个比特位又分为三部分:
符号位:占据最高位1个比特位。0-正,1-负。
指数位:占据高位8个比特位。
尾数:占据剩余的23个比特位。
浮点型的二进制采用科学计数法(将小数点挪到第一个二进制1的后边)。
小数的二进制计算方式——乘2取整法。
例如,0.25*2=0.5->整数部分是0
0.5*2=1->整数部分是1
因此,0.25的二进制就是0.01,科学计数法就是1.0*2^-2——小数点右移了2位。
同样,5.25的二进制就是101.01,科学计数法就是1.0101*2^2——小数点左移了2位。
5.25中,符号位-S:0正数;
指数位-E:2;
尾数-M:1.0101
![](https://i-blog.csdnimg.cn/blog_migrate/e61382c11699ec3ecb4cc7c1e38614fc.png)
因为,小数点总是在第一个1的后边,因此尾数部分实际存储时会忽略前边的1,在使用时再加上(为了多节省一个比特位),即:
![](https://i-blog.csdnimg.cn/blog_migrate/039b08144f79dd0a445f83deebd28259.png)
但是,以上存储方式存在一个问题:指数位是负数时的存储(因为指数位解释时是当做无符号解释的)。
🌸实际存储:在指数位以127作为中间基数,加上实际存储的指数数值,得到指数的实际存储。
5.25->1.0101*2^2 ->指数部分存储的就是127+2=129(1000 0001)
0.25->1.0*2^-2 ->指数部分存储的就是127+(-2)=125
因此,5.25的实际存储为:
![](https://i-blog.csdnimg.cn/blog_migrate/c9f8741bb6dedb72acc60955ec981d52.png)
即0x40 a8 00 00
float的数据存储范围:8个比特位的指数255(1111 1111),指数最大是128(255-127),范围为-2^128~2^128。
float尾数区域只有23个比特位,因此存储小数部分时可能会存在精度损失。
double类型:double有8个字节,64个比特位。
符号位:最高位1位。
指数位:高位11位,指数存储以中间数1023作为基数。
尾数位:剩余低52位。
double类型的存储数据范围:-2^1024~+2^1024。
尾数区域为52位,存储精度比float更高。
🌻浮点型的比较
浮点数不存在真正的0,只能表示一个无限接近于0的数值,且浮点数不能直接进行比较,而是以一个数值作为比较精度。
例如,
float num1,num2;
num1>num2&&num1-num2>0.0000001
指数部分全为0,表示的是一个正负0(其实是一个无限接近0的数字)。
指数部分全为1,并且尾数全为0,表示的是正负无穷大(并不表示特定值)。
#include<stdio.h>
#include<stdlib.h>
int main(){
int a = 9;
float *pf = (float*)&a;
printf("%d\n", a);
printf("%f\n", *pf);
system("pause");
return 0;
}
运行结果:
9
0.000000
🌸9->00000000 00000000 00000000 00001001
指数部分全为0(*2^0),因此代表的是0。