【问题标题】:Odd behaviour when using malloc使用 malloc 时的奇怪行为
【发布时间】:2018-04-10 17:26:06
【问题描述】:

我是 C 的新手,在使用 malloc 时遇到了奇怪的行为。

我从标准输入 (fgets) 读取输入文本并将其传递给函数 myfunction。

void myfunction(char* src) {

     printf("src: |%s|\n", src);

     int srcLength = strlen(src);
     printf("src length: %d\n", srcLength);

     // CAUSES ODD BEHAVIOR IN MY SITUATION
     // char* output = malloc(200);
     //
     // if (output == NULL) {
     //   exit(EXIT_FAILURE);
     // }

     for (int i=0; i < srcLength; ++i) {
       char currChar = src[i];
       printf("|%c| ", currChar);
     }
}

在没有 malloc 的情况下执行函数时(见评论),我得到这个:

src: |asdf|
src length: 4
|a| |s| |d| |f|

但是使用 malloc,我得到了这种尴尬的行为。好像 char* 中没有字符:

src: |asdf|
src length: 4
|| || || || 

char* src(来自标准输入)可能存在问题。但我不确定,因为输入字符串打印正确(src: |asdf|)。

谁能支持我,如何分析问题的根源?

更新 1:

这是从标准输入读取并调用 myfunction 的代码。

int main(int argc, char **argv) {

  char *input = NULL;
  input = readStdin();
  myfunction(input);
  return EXIT_SUCCESS;
}

char* readStdin(void) {
    char buffer[400];
    char *text = fgets(buffer, sizeof(buffer), stdin);
    return text;
}

myfunctionreadStdin 在不同的文件中,但我希望没关系。

更新 2:

根据 cmets 支持者的建议,我解决了范围问题。

我把readStdin的函数原型改成:

 char* readStdin(char* input);

然后我用分配的input 调用readStdin

 char* input = malloc(400);

readStdin我用函数参数替换了buffer

【问题讨论】:

  • 你能显示你从标准输入读取字符串的代码吗?如果将源字符串的打印移到 malloc 之后会发生什么?那些输出之间的唯一区别是您取消注释该代码行还是您更改了打印语句的来源?
  • 您需要发布minimal reproducible examplemalloc() 对您的代码没有明显影响,但如果您在其他地方损坏了堆,即使是看似无害的代码也可能导致出现潜在问题。
  • 如果malloc() 在src 的printf() 之前完成会怎样?我想知道是否仍然正确打印src
  • 所以,很明显你已经破坏了代码中其他地方的堆。请将代码发布在您从标准输入读取字符串的位置以及调用此方法的位置。如前所述,请发帖MCVE
  • 一旦您离开readStdinbuffer 将失效。访问它是未定义的行为,你应该避免。 (我猜问题不是malloc,而是附加变量output,恰好占据了buffer之前占用的部分空间,从而破坏了它。)

标签: c malloc stdin


【解决方案1】:

正如许多人在 cmets 中所说,您有范围问题。为避免这种情况,您需要在readStdin() 内分配内存。从 cmets 引用 @MOehm

一旦你离开了 readStdin,缓冲区就会变得无效。访问它是未定义的行为,您应该避免这种行为。 (我猜问题不是malloc,而是额外的变量输出,恰好占据了之前被buffer占用的部分空间,从而破坏了它。)

#define SIZE 400

char* readStdin(void) {
    char buffer[SIZE];
    char *text = NULL

    fgets(buffer, sizeof(buffer), stdin);
    text = malloc(sizeof(char) * (strlen(buffer) + 1));//allocate memory
    strcpy(text, buffer);//and copy the buffer into it.

    int length = strlen(text);
    if (text[length - 1] == '\n') {
        text[length - 1] = '\0';
    }
    return text;
}

你的 main 函数现在应该是这样的:

int main(int argc, char *argv[]) 
{
    char *input = NULL;
    input = readStdin();
    myfunction(input);
    free(input);//must now free it.
    input = NULL;
    return EXIT_SUCCESS;
}

尝试进行这些更改,您会发现所有问题都将迎刃而解。现在MyFunction 将按预期工作。

【讨论】:

    【解决方案2】:

    使用 malloc 时的奇怪行为

    是的,这很奇怪……也许不是。你的代码有未定义的行为,所以一切都可能发生。

    问题是,如果fgets 成功,text 最终将成为指向buffer 的指针。但是buffer 是函数中的一个局部变量,所以只要readStdin 返回变量buffer 就不再存在了。因此,您传递 myfunction 一个无效的指针,当您使用它(即用于读/写)时,您的行为未定义。

    一旦你有未定义的行为,就没有理由去推理正在发生的事情......但如果我们无论如何都尝试这样做,大多数系统的可能解释是:

    buffer 位于堆栈上。当readStdin 返回时,堆栈指针递减(或递增),因此buffer 现在位于堆栈的未使用部分。当你调用一个新函数时,新函数也需要一些堆栈空间。多少取决于函数使用的变量数量。换句话说 - 变量越多,需要的堆栈空间就越多。由于新变量将覆盖堆栈的某些部分 - 从而覆盖保存过时的 buffer 变量的部分内存 - buffer 的破坏量可能会随着函数调用中的变量数量而变化。这可能就是你所看到的。

    但请注意,上述解释是系统特定的。它不是 C 标准指定的东西。尽管如此,大多数系统都是这样工作的。

    怎么办?

    而不是

    char buffer[400];
    

    char* buffer = malloc(400);
    

    【讨论】:

    • 谢谢你也有效! :) 所以,char* buffer 将在readStdin 返回后存在,因为它是指向堆的指针,而char buffer[400] 只是本地的并且在堆栈上?
    • @Bernhard_S 完全正确....但请注意.... C 标准没有规定,但大多数系统都是这样工作的。 C标准根本没有提到堆栈和堆:-)
    【解决方案3】:

    我不明白为什么您在执行char* output = malloc(200); 时不会出现编译错误:您必须转换为char*,例如char* output = (char*) malloc(200);

    【讨论】:

    猜你喜欢
    • 2015-06-05
    • 2011-01-21
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多