对大端小端的一些理解
首先我们俩看一下什么是所谓的大端小端 :
计算机中所说的大端小端是指计算机的字节序, 也就是字节顺序模式 , 由于计算机中的数据以01构成的字节存储 , 所以会有大小端的问题 .
小端字节序的数据存储模式是低地址存低位, 即低位在前高位在后 .
大端字节序的数据存储模式是相反的,高地址存低位, 即高位在前, 低位在后.
一台机器的字节序是大端小端取决于CPU架构. 典型的有x86都是小端序, MIPS就是大端序, ARM默认是小端, 但可以选择大端, 在网络传输中, 都是大端序.
文字叙述太抽象我们来看图示 :
以十进制数 1 在计算机中存储为例(图中数字都为16进制)
大端 :
小端 :
那我们如何知道我们所用的计算机字节序为大端还是小端呢?
也非常简单 , 我们只需要很简单的一段代码就可以检测出我们所用的计算机字节序是哪一种 , 我们来看代码
#include<stdio.h>
#include<stdlib.h>
int main() {
int a = 1;
char b = a;
printf("%d\n", b);
system("pause");
return 0;
}
在32位系统VS编译器中int为4个字节 , 而char为1个字节 , 在这段代码中 , 将int型的a赋给char型的b , 就会发生截断 , 什么是截断呢? 截断 , 在这个代码中就是为char型的b分配的内存比int型所分配内存小 , 所以将int型给char型赋值(或强制类型转换时)会发生截取 , 截取从低地址位发生 ,往高地址位截取 ,所以凭借截断是从低地址位开始截取的 , 就可以判断出计算机的字节序是哪种, 如代码运行后为 :
按照前面十进制数 1在大端字节序和小端字节序中的图示 , 我们就可以判断出 , 这个计算机为小端字节序 , 反之 , 如果输出为0 ,则为大端字节序 .
还可以用联合(共用)体来判断计算机的字节序, 来看代码 :
联合(共用)体(点击跳转)
#include<stdio.h>
#include<stdlib.h>
union Un{
char c;
int num;
};
int main() {
union Un u;
u.num = 1;
printf("%d\n", u.c);
system("pause");
return 0;
}
运行结果如下 :
因为联合体中数据共用一段内存空间, 代码中定义的联合体只占四个字节, 当给int型的u.num 赋值为 1 时, 如u.c 的值为1, 则计机为字节序为小端序, 如果为 u.c 为 0的话, 则该计算机为字节序为大端序.
我们再来看道笔试题
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int*)(&a + 1);
int* ptr2 = (int*)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
我们先分析一下这道题
&a是取这个一维数组的地址 , 数值上与&a[0](数组首元素地址)的值一样 , &a类型为int(*)[4] , &a+1是跨过了整个数组 , 数值上等于&a[3]+1 , 指向了a[3]后面的与数组a相同大小但未申请的一片连续内存 , 此时再将&a+1强制类型转化为int* ,相当于此时不再指向a[3]后面的与数组a相同大小但未申请的一片连续内存, 而是指向a[3]后面一个为int型的内存空间 . 所以ptr1[-1]相当于向前偏移一个元素 , 指向的是a[3] 如图 :
a是这个一维数组的数组名 , 代表数组首地址,也是第一个元素的地址 , 数值上也和&a[0]相同 , a的类型为int* , 题目中先将a强制转化为int型 , 再加 1 , 再将这个值(int型)强值转化为int* 型, 我们知道 int型在32位系统VS编译器中为4个字节 , 这两个强转操作相当于指针ptr2指向了a[0]这个int型元素的内存中的第二个字节 , 再将int*型的ptr2解引用时 , 由于我这台计算机为小端 , 所以读取到的应该是图中红色框内的 02 00 00 00(16进制) 即为2000000(16进制下)
调试查看内存也是如此:
所以这段代码运行结果是