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
指针指向的对象类型的空间。由于res
是char**
,您应该为char*
类型提供malloc
空间。也就是说,在这种情况下,char*
类型是您应该提供给sizeof
的内容,无论您想要多少对象。 sizeof(char*)
或者更好的 sizeof(*res)
就像 Ted 提到的那样,因为无论 res
是什么类型的指针,*res
总是会给你正确的对象类型。
@TechTycho 我认为你错过了我所说的类型可能具有欺骗性的观点。字符串中填充了const char
s,因此对它们进行char*
可能会导致以后出现问题(有人可能会尝试更改字符串-它会编译得很好但会导致UB)。您也不需要在 C 中从 malloc
转换返回值,所以 const char **res = malloc(4 * sizeof *res);
就可以了。以上是关于malloc 函数的内存分配如何工作?的主要内容,如果未能解决你的问题,请参考以下文章
memcached 的内存分配器是如何工作的?为什么不适用 malloc/free!?为何要使用 slabs?