C语言中申请0内存与结构体中数组长度为0深入解析

最近在从事C语言发面的开发,发现自己的基础一般,工作中遇到了C语言中malloc申请长度为0的情况,此时居然返回的指针不是空,造成了业务终端的问题,同时对结构体中数组长度为0的内存分配不是很清楚,因此总结他人的博客和自己的见解进行了深入分析。

一、malloc申请0长度内存

        初始化在堆上malloc了一块区域,但是malloc()的size为0,这块地址被用来存了很多数据,数据依然可以使用,但是存的数据会将其他地址踩掉,就会在用其他地址的时候存在segment fault段错误问题

在标准的malloc实现中,并不检查输入值的大小,而是将输入值做对齐操作后直接从堆上分配空间。 
不论输入值的大小为多少,在malloc的内部最小的内存分配大小是一个定值(一般是8B),因为malloc需要用这部分空间来维护堆上的内存块链表。所以当用户申请一块0B的空间时,malloc实际分配的空间是8B,如果用户申请的空间是X,则malloc实际分配的空间是(对齐(X)+8)。这也是为什么malloc分配的空间千万不能越界使用的原因:堆的内部链表结构将被破坏。 
 
对于new和delete malloc和free这样的内存分配与释放函数:到底delete和free是怎么知道要释放掉多少内存的呢? 
其实在new和malloc内存分配成功时,系统除了返回一个指向这块内存的指针外,还会获得一块用于记录此处分配的内存大小的内存块 。

在内存管理中,内存被分为两部分:栈和堆

       栈有自己的机器指令,是一个先进后出的数据结构。 
malloc分配的内存是堆内存,由于堆没有自己的机器指令,所以要有系统自己编写算法来管理这片内存,通常的做法是用链表,在每片被分配的内存前加个表头,里面存储了被分配内存的起始地址和大小,你的malloc返回的就是表头里的起始指针,这个地址是由一系列的算法得来了,通常不会为0,一旦分配成功,就返回一个有效的指针,返回指针为NULL的条件是申请一定大小内存,内存空间不够的时候,对于分配0空间来说,算法已经算出可用内存的起始地址,但是你占用0空间,所以对那个指针操作就是错误的,操作系统一般不知道其终止地址,因为有占用大小就可以推出终止地址,还有就是即使分配0空间也要释放它,其实是释放的链表结点。

例如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    char* temp = NULL;
    temp = malloc(0);
    printf("the address of temp = %p\n", temp);
}

输出结果:

the address of temp = 0x685010
  • 1

说明malloc(0)是会返回malloc的地址,只是malloc的地址长度为0

总结:在一个函数中,如果申请内存的长度是入参,需要判断是否大于0 ,不然会出现意想不到的错误。

二、

        在标准C和C++中,长度为0的数组是被禁止使用的。不过在GNUC中,存在一个非常奇怪的用法,那就是长度为0的数组,比如Array[0]; 很多人可能觉得不可思议,长度为0的数组是没有什么意义的,不过在这儿,它表示的完全是另外的一层意思, 这个特性是不可移植的, 所以,如果你致力于编写可移植,或者是稍稍需要跨平台的代码,这些Trick最好还是收起来的好。

struct line {
int length;
char contents[0];
};

struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
thisline->length = this_length;

        这段代码的主要含义是定义了一个结构体,并对其进行初始化,上面结构体的第二个成员变量contents[0]事实上是不占内存空间的,因此整个结构体的长度sizeof(struct line)为4。当采用malloc为其申请内存空间时,如上所示,申请了一段长度为结构体长度加可变长度的内存空间给结构体类型的指针,这时contents就指向申请的可变长度的内存空间。由于是一次申请的,所以这段可变长度的内存空间和前面的结构体长度的内存空间是连续的。对于这段可变长度的内存空间,可以采用数组的方式对其进行访问。对于整个结构体,当不再使用时,可以使用free函数一次性对其进行释放,而不必像指针那样分别释放。

总结:

  1. 长度为0的数组并不占有内存空间,而指针方式需要占用内存空间。
  2.  对于长度为0数组,在申请内存空间时,采用一次性分配的原则进行;对于包含指针的结构体,才申请空间时需分别进行,释放时也需分别释放。
  3. 对于长度为0的数组的访问可采用数组方式进行。

 

 

 


版权声明:本文为fsfsfsdfsdfdr原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。