C语言中函数'fgets'的参数过少

3
每次我编译这个代码时,都会出现错误,但我不确定原因。我是直接从书上复制的。有人能帮我吗?
#include <stdio.h>
#include <string.h>

int main(void) {
    char buffer[256];

    printf("Enter your name and press enter:\n");
    fgets(buffer);

    printf("Your name has %d characters and spaces!", strlen(buffer));

    return 0;
}

6
这本书应该扔进垃圾箱。 - BLUEPIXY
3个回答

4

使用fgets()函数,因为gets()函数是缓冲区溢出漏洞的一个普遍来源,不应该使用。

char *fgets(char *str, int n, FILE *stream)
  • str -- 这是一个指向字符数组的指针,用于存储读取的字符串。
  • n -- 这是要读取的最大字符数(包括最后的空字符)。通常使用传递给 str 的数组的长度。
  • stream -- 这是一个指向 FILE 对象的指针,用于标识要从中读取字符的流。

2
是的。我认为这是打错了。 - Soner from The Ottoman Empire
1
函数fgets()会在读取的字符串末尾添加一个换行符。@AlexD - Soner from The Ottoman Empire
2
@itsnotmyrealname:“……在字符串末尾添加一个换行符”并不总是这样!这个缺失是一个标志,表明缓冲区太小了,下一个fgets将读取当前行的剩余部分。 - Jongware
1
提到的问题只有在具备特定条件下才能克服。没有必要大惊小怪。 - Soner from The Ottoman Empire
1
@itsnotmyrealname 顺便说一下,当前代码在从空文件重定向输入时存在 UB。根据我的测试,在这种情况下不会写入 null 终止符。但是即使创建了一个零长度字符串,buffer[strlen(buffer)-1] 也会导致 UB。 - AlexD
显示剩余5条评论

2

这本书的代码 fgets(buffer); 在使用标准库的 C 编译器中无法匹配。以下是已经更正的代码。

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

int main(void) {
  char buffer[256];
  printf("Enter your name and press enter:\n");
  // fgets(buffer);
  if (fgets(buffer, sizeof buffer, stdin) != NULL) {

    // Remove potential \n
    buffer[strcspn(buffer, "\n")] = 0;

    // printf("Your name has %d ...
    printf("Your name has %zu characters and spaces!\n", strlen(buffer));
  }
  return 0;
}
  1. fgets()有三个参数: buffer, size, stream。

    char *fgets(char * restrict s, int n, FILE * restrict stream);

  2. 使用I/O函数时,一定要检查返回值。

  3. printf(..."characters and spaces" ...)中,应该移除潜在的'\n',因为它会被视为没有出现过。

  4. strlen()的返回类型是size_t。要打印输出,应该使用格式说明符"%zu"


正如在https://dev59.com/hYvda4cB1Zd3GeqPfOLp#30791263的评论线程中所讨论的那样,`fgets`通常(虽然不总是)会添加`\n`。 - AlexD
@AlexD 详细说明:fgets() 读取的字符与其他任何字符一样,都不会添加 '\n'。它读取一定数量的字符,无论是 'A''\n' 还是 '\0' - 任何字符值 - 并将其存储在缓冲区中。读取 '\n' 是成功读取的三个终止条件之一(另外两个是:缓冲区已满,在数据后遇到 EOF)。fgets() 会向缓冲区添加一个终止的 '\0',使其成为 C 字符串。 - chux - Reinstate Monica
是的,我明白。我的意思是从用户的角度来看,输出看起来会有一个偏差,因为 strlen(buffer) 会将换行符视为名称中的一个字符。 - AlexD
@AlexD 很好的发现 Off-by-one_error:代码已更新以处理讨厌的 '\n' 并匹配“字符和空格”的上下文。 - chux - Reinstate Monica
“%zu”有什么特别之处?在编译过程中,我收到了两个警告(“未知的转换类型”和“格式参数太多”),并且我的输出打印出了“zu”,而不是数字。我用“%d”替换了“%zu”,数字按预期打印到输出中。 - harperville
如果您使用符合C99(或更高)标准的编译器,则 "%zu" 被指定为可以与从 strlen(buffer) 返回的 所有 可能值正确地配合使用。"%d" 可能适用于一些编译器和一些值——也可能不适用。这实际上取决于您希望代码有多可移植。如果必须使用旧的或不符合标准的编译器,则 printf("%lu\n", (unsigned long) strlen(buffer)); 是一个很好的替代方案。您使用哪个 C 编译器? - chux - Reinstate Monica

2

fgets函数需要三个参数。也许他们是指gets函数吗?


好的,已经解决了,谢谢。我猜那就是他们的意思。 - Walter Straub
2
根据C标准,是的。char *fgets(char * restrict s, int n, FILE * restrict stream); - AlexD
1
在版本之间,他们可能会采用“使用fgets而不是gets”的建议,并过于字面地理解了它。 - Jongware
1
@WalterStraub:不要使用 gets,它会在您的程序中引入失败点。它在1999年标准中被弃用,并已从最新的2011年标准中删除。这个库函数在过去40年中一直负责各种混乱。它是不安全和不安全的 设计,不应该在新代码中使用。 - John Bode
1
@aurelien 另一种选择是 gets_s - AlexD
显示剩余2条评论

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