我正在try 初始化一个由26个字符串组成的array.我不希望将数组放在堆上,但是当我try 使用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));

为什么会这样呢?

推荐答案

在使用calloc的代码片段中,您创建了27个数组:自动存储中的一个指针数组[1],堆上的26个字符array.

在使用memset的代码片段中,您创建了指针数组,仅此而已.您try 修改26个字符数组,但从未创建它们.

以下代码等同于calloc代码段,但仅使用自动存储.

char string00[  3 ] = { 0 };
char string01[  4 ] = { 0 };
char string02[  5 ] = { 0 };
char string03[  6 ] = { 0 };
char string04[  7 ] = { 0 };
char string05[  8 ] = { 0 };
char string06[  9 ] = { 0 };
char string07[ 10 ] = { 0 };
char string08[ 11 ] = { 0 };
char string09[ 12 ] = { 0 };
char string10[ 13 ] = { 0 };
char string11[ 14 ] = { 0 };
char string12[ 15 ] = { 0 };
char string13[ 16 ] = { 0 };
char string14[ 17 ] = { 0 };
char string15[ 18 ] = { 0 };
char string16[ 19 ] = { 0 };
char string17[ 20 ] = { 0 };
char string18[ 21 ] = { 0 };
char string19[ 22 ] = { 0 };
char string20[ 23 ] = { 0 };
char string21[ 24 ] = { 0 };
char string22[ 25 ] = { 0 };
char string23[ 26 ] = { 0 };
char string24[ 27 ] = { 0 };
char string25[ 28 ] = { 0 };
char *string_array[ 26 ] = {
   string00, string01, string02, string03, string04,
   string05, string06, string07, string08, string09,
   string10, string11, string12, string13, string14,
   string15, string16, string17, string18, string19,
   string20, string21, string22, string23, string24,
   string25,
};

现在,如果我们假设char没有对齐限制,我们可以将上面的简化如下:

char buffer[ 403 ] = { 0 };  // 3+4+5+...+28 = 403
char *string_array[ 26 ];
for ( size_t j=0, i=0; j<26; ++j ) {
   string_array[ j ] = buffer + i;
   i += j + 3;
}

最后,正如@Ted Lyngmo指出的那样,一些编译器在自动存储中提供了alloca/_alloca进行分配.

char *string_array[ 26 ];
for ( size_t j=0; j<26; ++j ) {
   string_array[ j ] = alloca( j + 3 );       // This is what you were missing.
   memset( string_array[ j ], 0, j + 3 );
}

C没有堆栈和堆的概念.C标准并没有规定对象存储在哪里,只规定了它们可以被访问的持续时间.这就是所谓的storage duration.

因此,以下内容不是基于标准,但几乎是普遍适用的:

objects with automatic storage duration objects with allocated storage duration
On the stack, in a register, or optimized away On the heap
Needs less time/CPU to allocate Needs more time/CPU to allocate
Needs less space/memory to allocate Needs more space/memory to allocate
Limited control over lifespan of the allocated block Lifespan can extend beyond the function that allocated the memory
Uncatchable dire consequences to over-allocating Over-allocating can be detected and handled
Somewhat limited resource Vast resource
Always available Not available on some systems (e.g. many embedded systems)
Can be hard/impossible to allocate complex structures unless the compiler provides non-standard alloca or similar. Simple to allocate complex structures

这里没有介绍两个存储持续时间:静态和线程.


  1. 不能保证使用堆栈.但没错,这可能会是一大堆.

C++相关问答推荐

在x86汇编中,为什么当分子来自RDRAND时DIV会引发异常?

如何创建由符号组成的垂直结果图形?

为什么在C中二维字符数组会有这样的行为?

为什么我会收到释放后堆使用错误?

在for循环中指向数组开头之前

在C中创建任意类型的只读指针参数

初始成员、公共初始序列、匿名联合和严格别名如何在C中交互?

为什么编译器不能简单地将数据从EDI转移到EAX?

使用正则表达式获取字符串中标记的开始和结束

在运行时判断C/C++指针是否指向只读内存(在Linux操作系统中)

在下面的C程序中,.Ap0是如何解释的?

unions 的原子成员是个好主意吗?

可以对两种 struct 类型中的任何一种进行操作的C函数

将char*数组深度复制到 struct 中?

从文件到链表读取日期

`%%的sscanf无法按预期工作

根据输入/输出将 C 编译过程分为预处理、编译、汇编和链接步骤

GnuCobol 使用 double 类型的参数调用 C 函数

C11 嵌套泛型

初始化动态分配的布尔二维数组的最佳方法是什么?