在C语言中动态分配字符串数组

3

我想了解C 99中动态分配数组的工作原理。无论如何,我有以下代码:

int main()
{
    char** members=malloc(4*sizeof(*members));
    *members=malloc(5);
    *members="john";
    *(members+1)="shouldn't work";
    printf("%s \n",*members);
    printf("%s",*(members+1));
    return 0;
}

我以为会因为没有分配(members+1)而产生运行时错误,但实际上它打印了"john"和"shouldn't work",而且看起来 *members=malloc(5)这一行是不必要的。为什么呢?

4个回答

5

您的指针赋值并不是想象中的那样。当您赋值*members或者*(members+1)时,您实际上是将char*赋值给每个字符串字面量,而不是将其复制到分配(或未分配)的堆内存中。

如果您使用strcpy替换赋值操作,例如:

strcpy(*members, "john");
strcpy(*(members+1), "shouldn't work");

在第二次赋值时会出现访问冲突,但第一次不会。同样,malloc(5)似乎是不必要的,因为您重新分配指针以指向字符串文字,而不是将“john”字符串复制到分配的内存中。


1
断言“第二个赋值将导致访问冲突”是错误的:他们可能会遇到访问违规,但代码也可能正常工作,或者在以后导致一些奇怪且难以调试的行为(包括标准认为的一些蓝色大象的出现)。这是未定义的行为,取决于存储在*(members + 1)中的任何内容,而不是确定性错误条件。 - cmaster - reinstate monica
@cmaster 对的,根据标准来说第二个 strcpy 是未定义行为,但是大多数现代操作系统会将新分配的内存页初始化为 0 以作为安全预防措施,所以解引用会导致 AV。如果进程之前已经释放了内存,则一切都不确定。 - MooseBoys

2
char** members=malloc(4*sizeof(*members));

这意味着你创建了一个数组并为其中四个元素分配了内存,可以用来存储字符串的地址。
因此,如果您删除行 *members=malloc(5);,它仍将正常工作。
int main()
{
char** members=malloc(4*sizeof(*members));
//*members=malloc(5);
*members="john";
*(members+1)="shouldn't work";
printf("%s \n",*members);
printf("%s",*(members+1));
return 0;
}

即成员可以容纳四个成员,每个成员可以容纳一个字符串的地址。

1

因为您已经用4*sizeof(*members)初始化了类型为char**的指针,我认为您计划存储4个字符串,建议为所有4个字符串分配内存,例如:

char** members=malloc(4*sizeof(*members));
int i;
for(i = 0; i < 4; i++)
    members[i] = malloc(100 + 1);  // 100 is a max length of string
              // actually length of each string can be different

或者您可以将所有指针初始化为NULL,并根据需要更改它们(分配或指向一些已分配的内存),例如:
char** members=malloc(4*sizeof(*members));
int i;
for(i = 0; i < 4; i++)
    members[i] = NULL;  
// ... somewhere ....
char str[] = "Some string";
members[2] = str;
// ... somewhere else ....
members[1] = malloc(20);
scanf("%19s", members[1]); 

在这种情况下,您可以使用if(members[i] != NULL)来识别指向数据的项目。


如果您有一个简单的char *值数组,那么通常需要知道哪些需要释放,哪些不能被释放。在您的示例中,您将拥有一些不可释放的字符串和一些可释放的字符串,这实际上有点混乱。您提出了一个有效的观点,即char *主题有多个变体,但保持一致是一个好主意,以便您可以可靠地释放空间。 - Jonathan Leffler
@JonathanLeffler:有时候奇怪的问题会产生奇怪的答案,这很正常 :-) - VolAnd

1
一些事情:

  1. 对于您的第一个malloc,malloc(4*sizeof(*members)),如果您使用您要分配空间的对象的类型,例如malloc(4 * sizeof(char *))而不是使用变量名,会更清晰。 编辑:正如cmaster指出的那样,如果您在不更改malloc()中的类型的情况下更改变量的类型,则可能会引发问题,因此请谨慎使用这个建议。
  2. *members=malloc(5);在您的情况下是不必要的,因为您的编译器已经为字符串"john"分配了空间,并且
  3. *members="john";通过将您的字符串数组索引0处的指针设置为编译器分配的"john"地址来工作
  4. 类似地,由于第3点提到的原因,*(members+1)="shouldn't work";实际上可以工作。
  5. 最后,由于这行 -- *members="john"; -- 您已经丢失了对malloc(5)的内存的指针,因此该内存将被泄漏 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接