为什么在使用 fgets() 时我们需要在声明它时删除字符串 -1?

Why when using fgets() do we need to remove the string -1 when we are declaring it?

为什么我们在声明函数 fgets() 时需要从原始字符中放入 -1 字符串?像这样:

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

int main ()
{
    char string[15]; 

    fgets(string,14,stdin); // < the original variable has 15 strings, but in fgets() we need to set 14, why?
    printf("%s\n", string);

    return 0;
    getchar();
}

我是C的初学者,所以我有这个疑问

不需要fgets 采用三个参数。

  • 要读入的 C 字符串缓冲区。
  • 字符数。
  • 要读取的文件流。

它最多将 count - 1 个字符读入缓冲区,从而为每个 C 字符串所需的空终止符留出空间。

附带说明:您对 getchar 的调用在 return 之后,因此完全没有意义,因为控制流永远不会到达它。

不需要fgets()的大小参数的数组长度中减去1。

第二个参数告诉 fgets 第一个参数指向的数组的长度,因此 fgets() 只在缓冲区边界内写入。 fgets() 最多将从流中读取比目标数组的长度少一个字节,并写入一个空终止符,使数组成为 C 字符串。

由于 sizeof(char) 根据定义是 1,因此在 C 中惯用的做法是将数组的大小作为 sizeof(string) 传递,但只有在 string 是实际数组,而不是指针。

除非流指针错误或在文件末尾,否则 fgets() 读取的字符串将以 null 终止。您应该测试 fgets() 的 return 值,以避免在无法从流中读取字符串时读取数组。

这是修改后的版本:

#include <stdio.h>

int main() {
    char string[15]; 

    while (fgets(string, sizeof string, stdin)) {
        printf(">>%s<<\n", string);
    }
    printf("Bye\n");
    return 0;
}

现在让我们运行程序并做一些测试:

  • 如果您键入一个短字符串 Hello!,然后按 Enter 键,您将看到 2 行输出:

    >>Hello!
    <<
    

    fgets() 读取字符串,包括尾随的换行符到 string 数组中并 printf 输出它,前面是 >>,后面是 <<和一个换行符,一次调用生成 2 行。

  • 如果您键入更长的字符串 I am testing this function relentlessly 后跟 Enter 键,fgets() 将最多读取 14 个字节时间,只有最后一个块会有尾随换行符:

    >>I am testing t<<
    >>his function r<<
    >>elentlessly
    <<
    
  • 最后,如果输入没有尾随换行符,例如:Yes! 后跟 Ctrl-DCtrl-D,数组 string 的末尾也没有换行符,下一次调用 fgets() 将 return NULL

    >>Yes!<<
    Bye!
    

Why when we are declaring the function fgets() we need to put -1 string from the original char?

我相信你说的是

的使用
    buffer[strlen(buffer) - 1] = 0;

在使用 fgets() 填充 buffer 之后,目的是从字符串中删除*预期的 \n。不是在“声明函数”时,而是在调用之后。

嗯,这是错误的

例子

#include <ctype.h>
#include <stdio.h>
int main(void)
{
    char      buf[5] = {0};
    const int n      = sizeof(buf);
    printf("Enter up to %d bytes: ", n);
    char* p = fgets(buf, n, stdin);
    while ((p != NULL) && (*p != '\n'))
    {
        printf("Buffer: ");
        for (int i = 0; i < n; i += 1)
            if (isprint((int)*(buf + i)))
                printf("%c ", *(buf + i));
            else
                printf("0x%0X ", *(buf + i));
        printf("\n\nEnter up to %d bytes: ", n);
        p = fgets(buf, n, stdin);
    };  // while()
    return 0;
}

此程序使用 fgetsstdin 读取字符串,缓冲区大小设置为 5。读取后显示 5 个字节。当不可打印时,它们以十六进制显示。 0x0A 是 \n

的代码

运行

Enter up to 5 bytes: 12
Buffer: 1 2 0xA 0x0 0x0

Enter up to 5 bytes:
SO>

12之后有一个换行符,在buf[2]

但是

Enter up to 5 bytes: 1234
Buffer: 1 2 3 4 0x0

Enter up to 5 bytes:
SO>

这里我们没有 \n,因为使用了所有 4 个字节,最后一个包含强制性 0,字符串的终止 NULL

'\n'留待下次阅读。程序结束,因为字符串开头的 ENTER 结束了循环。

如果用户输入所有 5 个建议字节,则相同,如

Enter up to 5 bytes: abcde
Buffer: a b c d 0x0

Enter up to 5 bytes: Buffer: e 0xA 0x0 d 0x0

Enter up to 5 bytes:

第二次调用显示 "5\n"。这次程序没有结束,因为 '\n' 在第二个字节上。

所以...

fgets() 会将 '\n' 保留在字符串的末尾,只要有 space 即可。如果所有字节都被占用,则剩余数据将留在输入缓冲区中。

fgetsreturns一个指针

如您在手册中所见,fgets() returns 指向缓冲区的指针或 NULL 以防出错或文件结束。如果出现错误,则设置 errno

如果 fgets 什么都没读到缓冲区根本就没有被触及,所以每天都有很多学生的程序挂起,因为程序不检查返回的指针并在出错后一遍又一遍地使用相同的数据或 EOF.