malloc 函数的内存分配如何工作?

Posted

技术标签:

【中文标题】malloc 函数的内存分配如何工作?【英文标题】:How is memory allocation on malloc function working? 【发布时间】:2022-01-17 21:59:07 【问题描述】:

char **res = (char **)malloc(sizeof(char *) * 1) 在这一行我使用了 sizeof(char *) * 1 但我放置了多个不同长度的字符串。我不明白这是如何工作的,或者只是我的编译器没有显示错误/警告还是正确的。

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

int main() 
    char **res = (char **)malloc(sizeof(char *) * 1);
    res[0] = "mang0000000o";
    res[1] = "tango00000";
    res[2] = "lango";
    res[3] = "django";
    for (int x = 0; x < 4; x++) 
        puts(res[x]);
        putchar('\n');
    
    return 0;

【问题讨论】:

当您的代码具有未定义的行为时,您的编译器不需要发出诊断(错误或警告),就像这里所做的那样。您的程序完全有可能至少在一段时间内似乎可以工作。有关更多信息和更多链接,请参阅here。 语法上这是正确的,但是您没有为分配的字符串分配足够的空间,因此您正在调用未定义的行为。将其更改为 sizeof(char *) * 4 GCC 警告你:godbolt.org/z/4vjMMx48G 它的“工作原理”与您可以将 10 个人塞进一辆 5 个人的汽车一样,但他们会将午餐洒在彼此身上。 -g -fsanitize=address,undefined 提供了良好的运行时信息。它直接指向res[1] = "tango00000";"AddressSanitizer: heap-buffer-overflow" 【参考方案1】:

在这种情况下,您已经为长度为 1 的指针数组分配了内存。当您转向一个您没有为其分配内存的元素时,您只是转向了第一个元素之后的下一块内存数组。如果您尝试在至少一百个元素的循环中执行此操作,您几乎肯定会遇到分段错误。当您访问尚未分配的内存时不会发生该错误,而是当您访问已被某人占用的内存时。

在这种情况下,您应该为 sizeof(char *) * (n + 1) 分配内存,其中 n 是您需要的元素数。您将 NULL 写入最后一个指针,以便可以方便地通过 while 或 for 进行迭代。

【讨论】:

很好的尝试回答但有一些问题:“100 个元素的循环”?在此之前,这可能是一个问题。这是res[1] = 的未定义行为(未定义行为是这里的关键短语)。您可以分配 n+1,也可以只分配 n 并跟踪 n。此外,数组是 char* 但字符串文字是 const 所以这可能是一个问题,具体取决于您尝试做什么。 @John3136 这是未定义行为的问题,所以c程序需要运行多次,最好在大量数据上运行。否则,您无法保证不会出现浮动错误。这就是为什么许多其他语言都放弃了内存管理,甚至在 C++ 中也几乎没有人直接通过 malloc 分配内存 我知道这一切,但尝试放置一些会导致未定义行为的东西是不可能的。如果您从数组末尾移出一个,我现在移植的代码会崩溃。任何人都不应该在 C++ 中使用 malloc - 他们应该使用 new。【参考方案2】:

这应该可行:

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

 int main() 
     const char **res = malloc(4 * sizeof *res); // Buff the size up a little bit
     res[0] = "mang0000000o";
     res[1] = "tango00000";
     res[2] = "lango";
     res[3] = "django";

     for (int x = 0; x < 4; x++) 
        puts(res[x]);
        putchar('\n'); // Note that 'puts' automatically adds a newline character '\n'
    
    
    free(res);

    return 0;

另请注意,您不一定需要 string.h 标头。 希望这对你有用。

【讨论】:

完全不需要你的* 15 我知道,这只是为了让它更容易,确定元素的最大尺寸。您可以将其移除并将“4”提升到 30 或其他值。 如果存储char* 类型的元素,为什么要使用sizeof char?您的缓冲区太小,无法在 64 位系统上容纳 4 个指针。原始代码实际上比这更接近解决方案。只有*1 应更改为*4 你想malloc 指针指向的对象类型的空间。由于reschar**,您应该为char* 类型提供malloc 空间。也就是说,在这种情况下,char* 类型是您应该提供给sizeof 的内容,无论您想要多少对象。 sizeof(char*) 或者更好的 sizeof(*res) 就像 Ted 提到的那样,因为无论 res 是什么类型的指针,*res 总是会给你正确的对象类型。 @TechTycho 我认为你错过了我所说的类型可能具有欺骗性的观点。字符串中填充了const chars,因此对它们进行char* 可能会导致以后出现问题(有人可能会尝试更改字符串-它会编译得很好但会导致UB)。您也不需要在 C 中从 malloc 转换返回值,所以 const char **res = malloc(4 * sizeof *res); 就可以了。

以上是关于malloc 函数的内存分配如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

memcached 的内存分配器是如何工作的?为什么不适用 malloc/free!?为何要使用 slabs?

memcached 的内存分配器是如何工作的?为什么不适用 malloc/free!?为何要使用 slabs?

c语言中啥是动态分配内存?

malloc函数分配内存失败的常见原因

malloc分配内存为多大?

C语言中动态内存分配函数的用法及作用?(比如malloc,calloc,realloc等)