使用常规方法创建数组和使用malloc创建数组的区别

4

我在学习时涉及到了函数中的C指向指针的操作。我创建了一个字符串数组,并希望通过指向指针的方式在函数foo中对其进行更改。然后我将其打印出来,只是为了看看结果。

问题在于:如果我正常地创建它:char array[] = "yeah",那么代码无法正常运行,控制台会输出一堆奇怪的字符。但是,如果我使用malloc来创建它,则可以正常工作。我真的想理解其中的区别。

这个可以工作:

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

void foo(char **ptr);

int main(int argc, char *argv[]) {
    char *array = malloc(sizeof("yeah"));  //I need to malloc here, or it doesn't work. Why ?
    strcpy(array, "yeah");

    printf("array before : %s \n", array);

    char *ptr = array;
    char **pt = &ptr;

    foo(&array);

    printf("array after : %s \n", array);

    free(array);
}

void foo(char **ptr) {
    char *truc = malloc(sizeof(char) * 20);  //I need to malloc here, else it doesn't work. Why ?
    strcpy(truc, "then");

    *ptr = truc;
}

但是这样做不行,控制台会显示诡异的字符:
void foo(char **ptr);

int main(int argc, char *argv[]) {
    char array[] = "yeah"; //created "normally"

    printf("array before : %s \n", array);

    char *ptr = array;
    char **pt = &ptr;

    foo(&array);

    printf("array after : %s \n", array);

    free(array);
}

void foo(char **ptr) {
    char truc = "then";

    *ptr = truc;
}

以下两者有什么不同:

char array[] = "yeah";

并且

char *array = malloc(sizeof("yeah");
strcpy(array, "yeah");

4
这段代码存在相当多的问题,使用gcc 8.2.0编译时会出现警告。我建议使用"-Wall"编译并尝试修复警告。 - anonmess
只要你将char array[] = "yeah";更改为char* array = "yeah";,它对我来说很好用。 - Michael Bianconi
1
一个数组和一个指针可能有相同的值,但一个是常量值,另一个是某个内存位置上的变量。int *a; a = malloc(...)int a[] = ... 之间的区别与 int x= 55 之间的区别相同。 - Lee Daniel Crocker
anonmess,是的,我不明白这些错误,作为一个菜鸟,我只在 code::blocks 上用 atom 编译,不用控制台 ^^' - adoprix
李·丹尼尔·克罗克,你知道为什么我需要在主函数中使用malloc才能让我的代码正常工作吗? - adoprix
你的“可用”代码存在内存泄漏。函数foo使用malloc获取一个新值并覆盖了main中的array指针。旧值是之前调用malloc时获取的,现在已经无法访问。foo(&array)表示:“这是我的非const指针的地址:如果你愿意,你可以覆盖它”,而foo正是这样做的,而没有释放它。 - Kaz
1个回答

2

数组的使用方式

首先需要注意的是:代码:char array[] = "yeah"; 是可行的。
注意:如果您这样做,就不需要调用 free(array) ,因为它不是指向动态分配内存的指针,也不需要动态返回到堆。它存在于栈上。请继续阅读...

但是这里的函数 foo() 存在问题。当调用 foo() 时,在一个栈帧中存在着字符串 truc(也可以声明为 char truc[] = "then";),这是程序内存的一部分,仅存在直到 foo() 返回。如果您将 array 更改为指向该栈帧内存中的内存,则当该函数返回时会发生什么?那个栈帧变得未定义,您指向的是无用的内存。

如果您想更改 array 中字符串的内容,可以确保缓冲区足够长,并且 foo() 可以通过 strcpy(array, "then") 将其复制到其中。因此,您不是更改指针本身,而只是更改了它所指向的内存。这并不是我所说的良好软件设计,但是作为示例,它可以为您提供帮助。如下所示:

void foo(char * ptr)
{
    strcpy(ptr, "then");
}

为什么不在main()中使用malloc()就无法工作呢?

当您在main()不使用malloc()时,array代表一个数组。标识符本身不是指针,而是数组;但是,它衰减成指针,或者说像一个指针。这就是为什么您可以将其传递给foo()等函数使用。现在,如果您引用array地址,例如&array(char*)array&array&array[0]都是相同的-指向array开头的指针。在这种情况下,您的foo()实际上是向array的内存中写入内存地址

这会做什么?嗯,技术上的答案是未定义的行为。但我猜它正在向曾经表示字符串中的4个8位字符的一块内存中写入32位整数(内存地址)。因此,现在您已经破坏了正好四个字符。如果foo()正在使用您展示的malloc(),还会引入内存泄漏。

这个有趣的部分是,您的字符串正好是四个字符,因此这不应该破坏字符串末尾的空终止符'\0'。让我猜猜,您看到了正好四个垃圾字符?


谢谢,但它并没有解释为什么当我在foo()中使用malloc而不是在main中使用时,代码仍然无法工作。有什么区别吗?我尝试了在foo中只使用一个malloc和在main中“正常”声明数组,但它仍然无法工作。无论如何,感谢您的快速回答! - adoprix
我原本想回复评论,但是我可以提供比评论更有帮助的解释。所以我进行了更新编辑。希望这能有所帮助! - Gutblender

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