【问题标题】:Pass by address, scanf, de-referencing按地址传递、scanf、取消引用
【发布时间】:2017-07-25 23:38:48
【问题描述】:

我编写了一个程序,采用以下概念:

我创建一个整数x 通过地址传递给一个函数,以及一个文件名,如果可用,该函数将打开文件,扫描第一行并将pX 指向的值设置为等于扫描的行。

只是它不起作用,我看不出我做错了什么。据我所知,下面的代码通常是如何完成它的,但我不确定我是否没有正确使用指针或什么scanf()

void foo() {
    char input[20] = "test.txt";
    int x = 1;
    bar(input, &x);
}

void bar(char *fileName, int *pX) {
    FILE *fp = fopen(fileName, "r");
    char *buffer = malloc(15 * sizeof(int));
    fgets(buffer, 15, fp);
    scanf(buffer, "%d", *pX);
    free(buffer);
    fclose(fp);
}

【问题讨论】:

  • scanf 函数需要参数的指针。 pX 是一个指针。 *pX 不是指针。
  • scanf(buffer, "%d", *pX);....编译器说了什么?
  • @SouravGhosh 编译。
  • 15 * sizeof(int) 您分配的大小超出了您的需要。您可能想要15 * sizeof(char),它与15 相同,因为sizeof(char) 等于1
  • 请调高您的编译器警告级别(-Wall -Wextra 代表 gccclang)。如果您的编译器没有提供正确的警告,请切换编译器。然后修复您收到的警告(如果您不理解它们或不知道如何修复,请在此处询问,很容易为简单的事情写一个高质量的问题)。

标签: c pointers pass-by-reference scanf


【解决方案1】:

您错误地使用了scanf():要么直接使用scanf 解析标准输入,要么使用sscanf() 解析fgets() 读取的字符串。此外,pX 已经是一个指向int 的指针,这是sscanf() 期望存储它转换的int 值,直接传递它:sscanf(buffer, "%d", pX);

这是修改后的版本:

int bar(const char *fileName, int *pX) {
    char buffer[15];
    FILE *fp = fopen(fileName, "r");
    int success = 0;

    if (fp != NULL) {
        fgets(buffer, sizeof buffer, fp);
        if (sscanf(buffer, "%d", pX) == 1)
            success = 1;
        fclose(fp);
    }
    return success;
}

void foo(void) {
    int x = 1;
    bar("test.txt", &x);
    /* do something with x */
}

注意事项:

  • 无需分配buf,只需将其设为具有自动存储功能的本地数组即可。
  • char *buffer = malloc(15 * sizeof(int)); 不正确:您为 15 个int 分配空间,而不是 15 个字符,根据定义,它们的大小为 1。使用目标类型的大小来避免任何不一致:

    char *buffer = malloc(15 * sizeof(*buffer));
    
  • 始终检查malloc() 的返回值以避免潜在的未定义行为。

  • fp 读取而不检查fopen 是否成功具有潜在的未定义行为。
  • filename指向的数组内容没有被修改,改为const char *
  • bar 返回成功指示符可能很有用。
  • 在编译时启用更多警告:gcc -Wall -Wextra -Werrorclang -Weverything -Werror 可能已经发现了 scanf 中的错误。

【讨论】:

  • 自动存储是什么意思?在我的实际应用程序中,我需要能够处理可能非常大的文件。基于整个文件大小的分配似乎是多余的,但却是确保实现这一点的有效方法。
  • 也感谢您提供包含“-Weverything”和“-Werror”参数的建议。这很棒,并且在未来会有很大的帮助。无论如何我可以永久打开这个选项吗?
  • @wanderbread:自动存储是函数范围内局部变量的默认设置,而不是使用 static 关键字的静态存储。一个小的缓冲区,最多几千字节就可以用于自动存储,更大的数组应该从堆中分配并在使用后释放。
【解决方案2】:

首先,C语言没有引用传递,函数参数都是值传递。通过传递一个指向数据的指针,我们模拟实现了与 pass-by-reference 相同的效果,但这并不意味着 C 有任何 pass-by-reference 的概念。

也就是说,问题似乎是

 scanf(buffer, "%d", *pX);
                     ^^^^

在哪里

  • 当前语法无效并调用undefined behavior。可能你需要sscanf()

  • px 已经是指向int 的指针。传递px 将是正确且足够的。

故事的寓意:启用编译器警告并注意它们。他们在那里是有原因的。启用适当的警告后,您应该会看到类似

警告:格式 %d 需要 int * 类型的参数,但参数 3 的类型为 int [-Wformat=]

最后,

  • 在使用文件指针之前,始终检查fopen() 的返回值是否成功。
  • 检查scanf()的返回值,确保扫描成功。
  • 检查fgets()的返回值以确保成功

...基本上,检查所有库调用的返回值以确保它们按预期工作

【讨论】:

  • 我试过这个。它似乎不起作用。至少在我正在编写的实际程序中。
  • scanf 这里应该是sscanf
  • @wanderbread 如果你已经知道了,太好了!!让我们让它出现在谁可能会更困惑的地方,好吗?
【解决方案3】:

换行:

scanf(buffer, "%d", *pX);

到:

sscanf(buffer, "%d", pX);

你需要函数 sscanf 来做你想做的事情。

scanfsscanf 都将指针作为参数。 pXint * 类型,因此是指向 int 的指针,应该适合您。你传递给*pX的是这个指针的内容,也就是int

另外,换行:

char *buffer = malloc(15 * sizeof(int));

到:

char *buffer = malloc(15 * sizeof(char));

或者简单地说:

char *buffer = malloc(15);

总是check the result of malloc

if (buffer == NULL){
    ...
}

【讨论】:

  • 不应该是:scanf(buffer, "%d", pX); ??
  • scanf(buffer, "%d", pX); 你确定这是有效的吗?
  • 谢谢。 sscanf(缓冲区,“%d”,px);解决了我的问题。
  • IMO,buffer = malloc(sizeof *buffer * 15); 更适合分配。无需编码匹配类型。
猜你喜欢
  • 2019-02-10
  • 2016-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多