我试图初始化一个26个字符串的数组。我不想把数组放在堆中,但是当我试图使用memset
给数组分配内存时,我得到了一个分段错误。重现这个错误的代码如下:
char *string_array[26];
for (int x = 0; x < 26; x++)
memset(string_array[x], 0, 3 + x); //= calloc(3+x, sizeof(char));
如果我修改代码,将内存分配给堆,使用calloc
,我不会得到分段错误:
char *string_array[26];
for (int x = 0; x < 26; x++)
string_array[x] = calloc(3 + x, sizeof(char));
为什么会这样?
5条答案
按热度按时间cl25kdpy1#
char *string_array[26];
只分配堆栈上的指针数组。然后使用memset初始化指针所指向的内容是未定义的行为,因为该内存尚未分配。
1u4esq0p2#
我假设OP的实现实际上有一个堆和一个栈
是否可以在堆栈上动态地分配这些指针?
***指针***已经在堆栈上了。如果你问你的指针指向的内存,那么是的。C语言有可变长度数组(Variable Length Arrays,简称VLA)--但是你有一个作用域的问题。一个自动变量(比如VLA)在声明它的作用域的末尾被销毁。
如果你想让动态分配的内存在栈上超过立即数作用域,并一直持续到函数返回,你可以使用
alloca
/_alloca
,这是一个非标准的函数,在栈上进行动态内存分配。它通常以下列形式之一出现:
示例:
lf5gs5x23#
在使用
calloc
的代码片段中,您创建了27个数组:在自动存储器[1]中有一个指针数组,在堆中有26个字符数组。在使用
memset
的代码片段中,您创建了指针数组,仅此而已。您尝试修改26个字符数组,但您从未创建它们。以下代码等同于calloc代码段,但只使用自动存储。
现在,如果我们假设
char
没有对齐限制,我们可以将上面的内容简化如下:最后,正如@Ted Lyngmo所指出的,一些编译器提供
alloca
/_alloca
来分配自动存储。C标准只讨论了 * 存储持续时间 *,它没有规定对象存储在哪里,只规定了可以访问对象的持续时间。
所以下面的结论并不是基于标准的,但下面的结论几乎是普遍正确的:
| 自动存储时间|分配存储持续时间|
| - ------|- ------|
| 更快|慢点|
| 更少的内存开销|更多内存开销|
| 对已分配数据块的生命周期的控制有限|生存期可以超出分配内存的函数|
| 过度分配的可怕后果|可以检测和处理过度分配|
| 资源有限|资源丰富|
| 随时可用|在某些系统上不可用(例如,许多嵌入式系统)|
| 除非编译器提供非标准的
alloca
或类似功能,否则很难/不可能分配复杂的结构。|易于分配复杂结构|有两个储存期限未在此处涵盖:静态和线程。
1.不能保证一定会用到堆栈,但是,很可能会用到堆栈。
jm2pwxwz4#
Memset用于使用特定值填充内存块。通常memset用于在重用内存块之前使用默认值重置内存块。Memset不是内存分配的替代品(即calloc和malloc)。这就是第一个代码段失败的原因,因为在未分配的内存范围内设置了值,而第二个代码段实际上正在分配内存。
gcmastyq5#
声明了一个指向
char
的指针数组[26]。注意这只为指针分配内存,而不是指向的数据。memset()函数用常量字节c填充s指向的内存区域的前n个字节。
你的代码调用了未定义的行为,因为它正在写入不属于它的内存。指针的内容是不确定的,也就是说,它们可能指向任何东西,进程访问指针所指向的内存是不法律的的。
另一方面,
calloc()
用于动态分配存储器。calloc()函数应为nelem元素数组分配未使用的空间,每个元素的字节大小为elsize。该空间应初始化为所有位0。