【C语言基础】大小端、数据存储
作者:互联网
大小端、数据存储
一、大小端问题:
为什么会有大小端问题?
在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit
但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的int型。另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
-
大端模式(Big-Endian):是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
-
小端模式(Little-Endian):是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
一个整形数0x12345678在大小端中的存放
一个整形数0x01234567在大小端中的存放:
优缺点:
-
大端模式:首先提取高位字节,可以看在偏移位置为0的字节来确定这个数字是 正数还是负数(不需要知道这个数据有多长)
-
小端模式:首先提取最低字节,提取一个,两个,四个或者更长字节数据的汇编指令以与其他所有格式相同的方式进行,因为地址偏移和字节数是一对一的关系,多重精度的数学函数就相对地容易写了。
检查计算机是大端还是小端?
int checkCPU(void)
{
union
{
int a;
char b;
}c;
c.a = 1;
return (c.b == 1);
}
由于联合体union的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用Little- endian还是Big-endian模式读写。
常见设备大小端:
STM32单片机:小端模式
STM8:大端
KEIL C51:大端
x86:小端
ARM既可以工作在大端模式,也可以工作在小端模式
注意:大小端模式是和硬件有关的,即和芯片本身有关,IDE不能进行设置。
什么时候需要考虑大小端:
相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换(就是通讯中的先发还是先收的问题)
原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。
二、数据存储
C语言运行时内存大致分为四个数据区:常量区、全局数据区(静态区)、堆区、栈区
2.1 常量区:
存储了未被作为初始化使用的字符串常量和 const 修饰的全局变量,特点是只能读不能写,受到操作系统运行时保护,强行修改会导致 segmentation fault,生命周期同程序运行过程。
/* 字符串 "hello world" 存在字符串常量区,
指针 str 本身存储在全局数据区或者栈上(函数内) */
char *str = "hello world";
2.2 全局数据/静态区
存储了全部的全局变量,和所有被 static 修饰的变量(包括全局和局部),其特点是生命周期很长(为一次程序的运行过程)并且只被初始化一次。
int global = 2; // 存在全局数据区
int main(int argc, char *argv[]){
static int static_global = 1; // 静态局部变量也是存在全局数据区
int local = 2; // 存在栈上,见下
return 0;
}
2.3 栈区
由编译器自动分配
是一个向低地址拓展的数据结构(后进先出)
存储了所有局部变量(不加static的),是先加载函数才能进行局部变量的定义,所以先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,仅仅是该变量所在函数的一次调用过程, 函数被调用时被自动分配并在函数返回后回收。
// num 和 localnum都是分配在栈上
int func(int num){
int localnum = -1;
return localnum * num;
}
2.4 堆区
由程序员分配
是一个向高地址拓展的数据结构,不连续
由操作系统负责维护的大片内存池,使用时需手动申请, 一般是调用 malloc 家族函数进行动态内存分配,但使用完毕后需要使用 free 手动释放,否则会造成严重的内存泄漏。所有分配的内存当该进程退出后就会被操作系统回收,但是对于需要长期运行的服务器程序来说,就必须保证内存泄漏尽量少。
int *p = (int*)malloc(100 * sizeof(int));
for(int i = 0; i < 100; i++){
p[i] = i;
}
free(p);
p = NULL;
2.5 堆和栈的联系
一般情况下,栈的空间小于堆,栈不会产生碎片,因为其空间连续,但是堆采用链表存储方式,会产生碎片。(链表就是一堆首位用指针跳来跳去相连的不连续数组)
void function()
{
int *p = (int *)malloc(10*sizeof(int));
}
在这一句代码中同时包含了栈和堆,定义的指针变量p是局部变量,在栈区中占用4字节空间,用来存放malloc动态分配的一块大小为40字节(32位系统中)的空间的首地址
为啥内存中既然有了栈,为啥还用堆这种内存空间。因为栈的工作的方式是先分配内存的变量后面才释放(先进后出),是从上往下填充(高内存地址到低内存地址)。但是很多变量不是单独存在的,可能和其他的变量嵌套,这样就和变量的生命周期起了冲突,为了解决这个问题,堆的设计就是从下往上分配,保证了栈中先进后出的规则不与变量的生命周期起冲突。为啥堆能解决冲突,还要设计栈这个结构呢,因为全部变量保存在堆中,会使得应用程序性能下降。
参考:
https://zhuanlan.zhihu.com/p/55241632
标签:小端,存储,字节,int,C语言,地址,内存,大小,数据 来源: https://www.cnblogs.com/liaozhelin/p/16290635.html