1. 基本概念
(1) 数据类型与存储
- 8bits = 1byte, 一个字节,就是一个char,2**8=256,最高位不用是 128;
- 8bits(比特位 / 位) = 1byte(字节);
- 16进制需要 1 2 4 8 2^4=16,4位二进制01(bits)来表示,也就是半字节。
- 一个char 占用 1 bytes,正好需要2位16进制来表示。对应颜色的 (10)255 = (16)FF;
- 大小端都要保证一个char是完整的。
- 一个 int 占用 4 bytes,需要 8 个16进制位来表示。
- 0x12345678; 把int写成16进制,每2位都是一个bytes,是内存的直接状态: 0x12 0x34 0x56 0x78
(2) 高位(MSB) /低位(LSB)
比如 int i = 0x12345678;
- 高位/影响最大的位 most significant bit: 12 相当于万位
- 低位/影响最小的位 least significant bit: 78 相当于个位
对于一个很大的数字,高位变化1,影响很大;低位变化1,基本没啥影响。
(3) 内存高低地址
可以打印出地址比较大小。
高低是一个相对量。
比如,0x01 和 0x 02 比较,前置就是低地址,后者是高地址。
(3) 大小端
字节(2个16进制位)是大小端的基本单位。
字节内部没有大小端区分。
不同变量之间也没有大小端的概念。
只有一个变量内部,在内存中表示时,才牵涉到高位放在高地址还是低地址的问题。这是CPU决定的。
- 基于分层的概念,数值读写一般不需要关注存储细节,除非有强制类型转换。
- 读取肯定要和最初的写入一致,要读出 0x12345678。
- 在内存中:
- 大端(低地址 放高位/万位) |0x12|0x34|0x56|0x78|----| 低地址 -----> 高地址
- 小端(低地址 放低位/个位) |----|0x78|0x56|0x34|0x12| 低地址 -----> 高地址
- 优劣:
- 大端模式比较符合人的直观习惯: 大端就像我们手写一串数字,先写百位,再写十位,再写个位,依次放到内存中:地址由小向大增加,而数据从高位往低位放
- 但小端模式是intel的选择。更符合逻辑: 低地址的不重要(least significant bit),高地址的重要(most significant bit)。
(4) 读取时:
- 1)大端依次读出,小端倒叙读出。读取时,字节内部是一个整体不受倒叙影响。
- 2)读取后,就和之前未存储时顺序一致了,也就是数没发生变化。
2. 检测方法
#include<iostream>
// **********************1
void test1(){
short int x;
char x0,x1;
x=0x1122;
x0=((char*)&x)[0]; //低地址单元
x1=((char*)&x)[1]; //高地址单元
printf("x0=%d,%c\t x1=%d,%c \n", x0,x0, x1,x1); //34 17
// 低地址存的是低位(22 相当于是个位 Least significant end),小端
}
// **********************2
// 共用体检测
union myunion{
int a;
char b;
};
int isLittleU(){
union myunion s1; //共用体测试
s1.a=0x10000000;
if(s1.b==0x10){
return 0; //big
}else if(s1.b==0x00){
return 1; //little
}
return -1;
}
//指针检测
int isLittleP(){
int a;
a=0x10000001; //指针测试
char b=*((char*)(&a));
//printf(">> b=%d\n", b);
if(b==0x10){
return 0; //printf("Big\n");
}else if(b==0x01){
return 1; //printf("little\n");
}
return -1;
}
void test2(){
//代码对,分析错的博客: https://www.jianshu.com/p/47dc814c9146
// 分析合理: https://blog.csdn.net/a6333230/article/details/117117919
std::cout << "is little? union method: " << isLittleU() << std::endl;
std::cout << "is little? pointer method: " << isLittleP() << std::endl;
std::cout << std::endl;
}
// **********************3
void test3(){
// https://blog.csdn.net/wwwlyj123321/article/details/100066463
//int hexInt=0x01020306;
int hexInt=0x41426162; //ABab ->10进制 65 66 97 98 ->16进制 41 42 61 62
//获取int的地址,转为指向char的指针
char *p2=(char*) &hexInt; // 指针方式其实就是共用体的本质
printf("hexInt=%x\n", hexInt);
printf("length, char*: %d, char: %d, int: %d\n", sizeof(p2), sizeof(*p2), sizeof(int));
printf("\n\n");
for(int i=0; i<4; i++){
// 逐个打印char,不行 %c
// 逐个打印 int,可以?
printf("[%d](%p) %d %c\n",i, ( p2 +i), *( p2 +i), *( p2 +i) );
}
}
int main(int argc, char *argv[]){
void printAscii(int colNumber);
// 直接打印,int to char 失败。有人说不能强制转换,要使用指针。
test1();
test2();
test3();
//part2: 如果有额外参数,则打印ascii 码
if(argc>1){
// 打印参数列表
printf("\n>>argc:%d\n", argc);
for(int i=0; i<argc; i++){
printf("argv[%d]=%s\n", i, argv[i]);
}
printAscii(9);
}
return 0;
}
//打印 ascii 码: 按 128*2 打印,虽然128后面大多是空的
void printAscii(int colNumber){
for(int i=0; i<255; i++){
printf("[%d]%c\t", i, i);
if(i%colNumber==0){
printf("\n");
}
}
}
$ g++ -std=c++11 a11.cpp
输出:
3. 影响
搞明白了,我确实不需要花时间搞懂这个大小端。
只有非8bits机器,或者网络字节流需要考虑
数值计算,只需要知道c++数据类型、函数、传参、引用、指针、特别是高维指针、返回、文件IO这个子集就可以了,需要改写了还要知道class等。