在函数中调用 char* var (假设全局访问) = fgets(..) 后,var 中的值在从 main 调用函数后意外更改

After invoking char* var (assume global access) = fgets(..) inside a function, the value inside var changes unexpectedly after function call from main

提问人:Sai Surisetti 提问时间:9/1/2022 最后编辑:Sai Surisetti 更新时间:9/1/2022 访问量:56

问:

[编辑:显然,从fgets获得的返回值只是正在使用的char buffer[SIZE]的地址,所以在我的情况下,这是有道理的,因为我在本地声明了它(因此,块范围)。PS:感谢所有的帮助!

我只是倾向于,一旦编译器完成执行调用 fgets 的函数,fgets 存储的数据就不会保持不变。

这背后的原因是什么?fgets 的返回数据是否有寿命或其他东西?如果是这样,他们是如何编写诸如 fgets 之类的函数的?有什么东西可以跟踪用户的调用范围?

代码如下:

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

#define SIZE 10

void input_string (char** pptr_one, char** pptr_two)
{
    char buffer[SIZE];
    printf("\nInput: ");
    char* x = fgets(buffer, SIZE, stdin);
    
    //Copying the original fgets result address:
    *pptr_one = x;
    //Copying its contents to another variable:
    *pptr_two = malloc(SIZE);
    strcpy(*pptr_two, x);
}

int main (int argc, char **argv)
{
    char* ptr_one;
    char* ptr_two;
    input_string(&ptr_one, &ptr_two);

    printf("\nptr_one is pointing to: <%s>\n", ptr_one);
    printf("ptr_two is pointing to: <%s>\n", ptr_two);

    return 0;
}
C 字符串 io fgets stdio

评论

3赞 Fe2O3 9/1/2022
换句话说,你把一个“局部变量”的地址传递给了 ,我们都希望它能完成它的工作,为你填满那个缓冲区。当您的函数返回时,“本地存储”蒸发了......在函数退出后保留局部变量的地址不是一个好主意......你总能找到关于将“buffer”声明为“静态”的信息......看一看......fgets()
0赞 Eric Postpischil 9/1/2022
Re “因此,函数范围”:只有标签才有函数范围。在函数主体内声明的标识符具有块作用域。goto
0赞 Sai Surisetti 9/1/2022
@EricPostpischil明白了,谢谢。我将重新编辑帖子。我不知道确切的术语..

答:

2赞 Wander Nauta 9/1/2022 #1

您(间接)设置为 的地址,该地址在结束时停止存在。*pptr_onebufferinput_string

没什么特别的;它只是返回它关于成功的第一个参数,在本例中是 ;之后,所有内存都指向函数本地的同一段内存。fgetsbufferx*pptr_one

参考

评论

0赞 Sai Surisetti 9/1/2022
哦,等等,原来是这样!所以,它本质上是返回指向先前声明的 char 缓冲区数组地址的东西?等等,那么,当您可以简单地访问刚刚创建的 char 缓冲区数组时,即使从 fgets 获取返回值的意义何在?顺便说一句,谢谢。
1赞 Wander Nauta 9/1/2022
fgets返回成功时的第一个参数或失败时的空指针;返回值允许您检查它是成功还是失败(您应该检查)。为什么第一个参数而不是其他非空参数?很难说,但有时很方便。(char *)1
4赞 programmer 9/1/2022 #2

缓冲区具有自动存储持续时间。char buffer[SIZE]

这意味着在函数返回后,与之关联的内存将被释放,并且由于它是局部变量而不再存在。input_string()bufferbuffer

溶液:

您可以使用以下命令分配内存:buffermalloc()

  • char *buffer = malloc(sizeof(char) * SIZE)

或者您可以声明为:bufferstatic

  • static char buffer[SIZE];
0赞 Fe2O3 9/1/2022 #3
void input_string (char** pptr_one, char** pptr_two)
{
    char buffer[SIZE];
    printf("\nInput: ");
    char* x = fgets(buffer, SIZE, stdin);
    // missing test for NULL return from fgets()

    /*** DO NOT DO THIS.
     * DO NOT TAKE THE ADDRESS OF A LOCAL VARIABLE FOR USE AFTER FUNCTION EXITS!
     * When successful, fgets will return the address of 'buffer' so x points to buffer.
    //Copying the original fgets result address:
    *pptr_one = x;
    *****/

    //Copying its contents to another variable:
    *pptr_two = malloc(SIZE);
    // should check that malloc succeeded
    strcpy(*pptr_two, x);
    // Aren't you glad that fgets() guarantees buffer will be a null terminated string?

    // Be sure caller free's the allocated piece of heap storage!
}

或者,如果您想使用该信息,请使缓冲区静态...

    static char buffer[SIZE];
    printf("\nInput: ");

如果使用静态内存块,请勿释放地址。

回顾 OP,您写道:“我刚刚了解到 fgets 存储的数据doesn't stay the same once the ... [execution of] the function in which fgets is being invoked."

所有数据都有持续时间。有些是磁盘上文件的持续时间,有些是程序执行的整个持续时间,有些是函数调用的短暂持续时间。

你的“缓冲区”在结束时蒸发并返回到它的调用函数('main()')。挂住数据的另一种方法是定义数据,并将缓冲区及其长度传递给 。input_string()main()input_string()

void input_string( char *buf, size_t len ) {
    fgets(buffer, len, stdin);
}

int main() {
    char buffer[ SIZE ];
    input_string( buffer, sizeof buffer );
    printf( "Got '%s' from stdin\n", buffer );
}

以上,完成,但数据仍然可用。input_string()

评论

0赞 Eric Postpischil 9/1/2022
@SaiSurisetti:避免使用这种方式。此类静态对象可防止在多个线程中使用该函数,因为不同的线程可能会同时尝试使用该静态对象。或者,单个线程可能希望同时保存来自函数的两个或多个结果。您应该学习动态分配内存的正确技术。static
0赞 Sai Surisetti 9/1/2022
@EricPostpischil是的,我刚刚意识到这一点,如果我们在这种情况下使用静态,我们最终会丢失之前的输入读取。我认为 strcpy 或在 main 中分配缓冲区是实用的。
0赞 Fe2O3 9/1/2022
@EricPostpischil 当然,“嵌入式系统”通常禁止使用动态分配......有各种各样的方式、方法和要求......:-)