user-0 提问: 求教，关于malloc给结构体分配内存大小的不解

回复:
user: user-0
content: 补充一篇自己写的总结： “C语言malloc(size)分配的可用空间，以及 free(*) 成功的根据”， https://blog.csdn.net/weixin_44732817/article/details/88566962

user: user-0
content: 引用 9 楼 gs汗宝宝 的回复:我觉得你是在说内存对齐的问题，这个网上有一些介绍还不错。  嗯，从某种角度来说，跟内存对齐也有关联。 这次无意当中因为错误使用 malloc() 而反过来查证，基本上理清了 malloc() 的实际操作了，也对 free(pointer) 为什么能恰好释放合适的内存块而不会多释放也不会少释放有了更清楚的认识，也算是大有收获。 也谢谢您的建议！

user: user-1
content: 我觉得你是在说内存对齐的问题，这个网上有一些介绍还不错。

user: user-0
content: 引用 7 楼 火花20180731 的回复:我是不太明白啊，你这个head = (pnum)malloc(sizeof(pnum));是几个意思？  你的节点类型是struct NUM类型，你给它取了个别名叫num，那么你创建节点应该是sizeof(num)的长度。 里面有一个int成员，一个pnum成员，所占内存大小是这两个成员的长度之和。 但是你创建节点却分配的是一个pnum的长度的内存，也就是struct NUM *类型。  你这还pend->a = i; pend->next = NULL;  赋值赋得倒是不亦乐乎...... 程序没崩我也是见了鬼了！  帖子第一句就说了，原本的代码是之前另外一个帖子的提问者给的，他当时就是在创建链表的函数里写成这样的：   head = (pnum)malloc(sizeof(pnum));  而且他自己编译通过了，运行的时候可以创建链表、读取并输出链表信息（结构体成员的值），只是最后进行到 删除链表的函数的时候才报错了。而他的机器上，报错的原因 和 我的机器上 报错的原因 ，不一样。 而且，我把他原来的代码的逻辑错误改正之后，运行就没有出错了。甚至把申请内存空间的部分代码改成 malloc(0) 也没问题。 所以，才会有这篇延伸探究 malloc() 具体实现的帖子。 以前，我也只知道 malloc(size) 是申请 size 个字节的内存块，就想当然地以为获得的也仅仅是 size 个字节的内存块。 但是，上面的代码所运行表现出来的情况证明不是这样子的，所以才慢慢深入 malloc().c 这个源头，知道了 malloc(size) 分配出来的内存块的大小，并不简单地 等于 size 个字节。虽然，知道了这些实现的细节，在现在看来似乎没有什么用处，但是我觉得学编程，特别是C语言的编程，还是不能脱离底层的基础知识，不能太依赖于抽象的概念和接口，那样学到的东西比较像空中楼阁，基础不牢。 好吧，顺便多罗嗦了几句，觉得不合适的话就请忽略掉吧。

user: user-2
content: 我是不太明白啊，你这个head = (pnum)malloc(sizeof(pnum));是几个意思？  你的节点类型是struct NUM类型，你给它取了个别名叫num，那么你创建节点应该是sizeof(num)的长度。 里面有一个int成员，一个pnum成员，所占内存大小是这两个成员的长度之和。 但是你创建节点却分配的是一个pnum的长度的内存，也就是struct NUM *类型。  你这还pend->a = i; pend->next = NULL;  赋值赋得倒是不亦乐乎...... 程序没崩我也是见了鬼了！

user: user-0
content: 谢谢指导。上午的结论还是比较粗浅的，因为之前对malloc关心得不多。博客里当时有个结论也写错了，在64-bit、size_t为8个bytes的系统里，malloc()成功得到的内存块的大小不是32bytes的正整数倍，而应该是 32 + 2 * sizeof(size_t) * n 个bytes，其中n是大于等于 0 的正整数。我刚刚把文章里的相关内容修改过了。  关于您上面回复的2点，我之前是这样想的： （1）之所以在上面的帖子里下第1点结论，是因为当时想理清到底malloc(0)分配了多大的内存空间。而且除了结构体本身使用到的之外，两个连续地址之间多出来的16个字节到底是不是malloc()分配的呢？后来，了解到malloc()还有对齐、overhead这些东西，才推断应该是连续的，中间多出来的16个字节也是malloc()分配的。当然了，这只是为了解决头脑里的困惑，就算知道是分配到的内存块的一部分，也是不能去动的。 （2）访问或者查看额外的内存地址上的东西，主要是因为调试的时候看到了一些不是代码里能提现出来的数据，所以就想搞清楚那些是什么，主要还是为了调试的时候明白一点吧，不是真的想在代码里去访问越界的内存。  再次感谢！

user: user-3
content: 你博客里的结论基本都对，但帖子里的疏漏太多了： 1. 连续使用malloc()给指针分配内存块的时候，分配的内存块确实是连续的 ——不应该有这种假定，这种假定既危险也无实际的意义。  2. 能访问内存不代表是合法的，比如你申请一个32字节的内存，你实际用了64字节。这就是典型的内存越界，内存越界不见得会立即出现异常，但是依然是非法访问内存。

user: user-0
content: 引用 3 楼 niiiloc 的回复:混淆了很多概念，相邻申请内存块的偏移值不等于申请内存块的大小。  从malloc.c的描述来看，如果分配到的内存块确实是相邻的话，确实是地址的差。  因为，分配的内存块里，除了有用户或代码需要存放数据的空间之外，还有 “对齐操作”的空间 和 “overhead” 的空间。 所以，并不因为分配的相邻内存块的地址差大于申请的size字节数，就不相邻。 只不过是用户代码“应该”访问的内存地址是不相邻的。  下午把这篇帖子重新整理了一下，请指导： https://blog.csdn.net/weixin_44732817/article/details/88530993

user: user-3
content: 混淆了很多概念，相邻申请内存块的偏移值不等于申请内存块的大小。

user: user-0
content: 既然没有人回复，那还是我自己来回答吧。  刚刚查阅了glibc里的malloc.c文件，里面介绍了malloc()在64位系统、size_t为8字节的情况下，最小分配块的大小是32字节。  在线查看C/C++库文件的网页：https://code.woboq.org/userspace/glibc/malloc/malloc.c.html   //  glibc/malloc/malloc.c /* * Vital statistics:   Supported pointer representation:       4 or 8 bytes   Supported size_t  representation:       4 or 8 bytes        Note that size_t is allowed to be 4 bytes even if pointers are 8.        You can adjust this by defining INTERNAL_SIZE_T   Alignment:                              2 * sizeof(size_t) (default)        (i.e., 8 byte alignment with 4byte size_t). This suffices for        nearly all current machines and C compilers. However, you can        define MALLOC_ALIGNMENT to be wider than this if necessary.    Minimum overhead per allocated chunk:   4 or 8 bytes        Each malloced chunk has a hidden word of overhead holding size        and status information.    Minimum allocated size: 4-byte ptrs:  16 bytes    (including 4 overhead)                           8-byte ptrs:  24/32 bytes (including, 4/8 overhead)        When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte        ptrs but 4 byte size) or 24 (for 8/8) additional bytes are        needed; 4 (8) for a trailing size field and 8 (16) bytes for        free list pointers. Thus, the minimum allocatable size is        16/24/32 bytes.         Even a request for zero bytes (i.e., malloc(0)) returns a        pointer to something of the minimum allocatable size.  */   简而言之，malloc()分配到的最小内存块的大小是8bytes的倍数，具体在64-bit系统、size_t的大小定义为8bytes（64-bit OS默认；此值可修改)时，分配到的最小内存块的大小是16bytes的倍数，实际再加上还有overhead占用的空间和Alignment（对齐）的要求，其实是32bytes的倍数。  下面用一段代码来验证：   // 测试最小分配到的内存空间大小，是否为32bytes // 测试环境OS为64bit，size_t为8bytes  #include <stdio.h> #include <malloc.h>  typedef struct mall_test {     int a;     struct mall_test *next; } MT, *PMT;  int main(int argc, char const *argv[]) {     char *test;  //  指针变量     printf("size of type : size_t is %ld\n", sizeof(size_t));     printf("size of type : int is %ld\n", sizeof(int));     printf("size of type : unsigned int is %ld\n", sizeof(unsigned int));     printf("size of type : * is %ld\n", sizeof(test));     printf("size of type : struct mall_test is %ld\n", sizeof(MT));      // 申请内存空间     PMT p[5] = {NULL};     for (int i = 0; i < 5; ++i)     {         p[i] = (PMT)malloc(0);         if (NULL == p[i])             printf("p[%d] malloc failed.\n", i);         else         {             p[i]->a = i + 1;             p[i]->next = NULL;             printf("p[%d] = (char*)malloc(0) returns hex address of p[%d] : %x\n", i, i, p[i]);         }     }      // 访问成员     for (int i = 0; i < 5; ++i)     {         printf("p[%d]->a is %d\n",i,p[i]->a);     }      // 释放内存空间     for (int i = 0; i < 5; ++i)     {         free(p[i]);         p[i] = NULL;     }      return 0; }   运行结果截图：    总结：  （1）64-bit操作系统、按 size_t 默认为8bytes的情况，malloc()分配到的最小内存块的大小是 32bytes。 （2）即使是 malloc(0) 也是。 （3）分配到的内存空间，可以按正常方式访问：比如 p[i]->a 访问结构体成员。 （4）也许不同的编译器对使用malloc(size)时，参数size 有不同的检查要求吧，看起来gcc没有要求太多，好像是非负整数就行。  暂时就想到这么多，应该算是解决了心里的一个小疑惑吧！

user: user-0
content: 关于内存段访问错误的原因，已经通过gdb调试找到了，是删除函数的逻辑错误造成的。    但是，这又和那位提问者所反馈的修改了代码之后，仍然出现和之前一样的错误的情况想矛盾了。现在想来，可能是操作系统和编译器的原因。  但是malloc到底分配多大的内存这个问题，还是存在。再次恳请有了解这方面情况的专家不吝赐教！谢谢！