C fgets 字符串长度不限于输入

C fgets string length not limited to input

提问人:Helix Development 提问时间:11/6/2023 更新时间:11/6/2023 访问量:76

问:

我很困惑,即使我提供的 char 数组最多只能有 5 个字符长,但如果提供更大的限制,长度也可以超过 5 个字符。mystringfgets

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

int main() {
    char mystring[5];

    fgets(mystring, 10, stdin);

    printf("len: %d", strlen(mystring));
}

如果我输入 8 个或更多字符并按 Enter,则长度为 9。考虑到数组的长度设置为 5,这是怎么发生的?mystringmystring

C 缓冲液 stdin

评论

8赞 273K 11/6/2023
这是未定义的行为。UB会发生什么无法解释。
1赞 Brad Lanam 11/6/2023
mystring 被覆盖后堆栈上的任何内容。这不是一个安全的操作。fgets 的第二个论点应该是:这样就不会发生这种情况。sizeof (mystring)
3赞 Jonathan Leffler 11/6/2023
你fib(说谎)到编译器;编译器通过向你发出 fibbing 来找回自己的结果。你说数组有 10 个字节长;事实并非如此。你喋喋不休;你得到了你应得的!
1赞 Tom Karzes 11/6/2023
试着理解 C 是一种编译语言。当您编译程序时,它会直接转换为机器指令。训练轮关闭。如果你写过数组的末尾,它可能看起来有效(在某种程度上),它可能会崩溃,或者可能发生任何其他事情。这是未定义的行为。它不会为每个数组引用发出代码,以查看是否在数组边界之外进行索引。如果需要,可以使用解释性语言为您进行这些检查。
0赞 hyde 11/6/2023
C 数组与其他语言中的数组完全不同。字符串也一样。说 C 没有数组(或字符串),只有上面有一些糖的指针,这并不完全夸张。

答:

1赞 Chris 11/6/2023 #1

当您只有 5 个有效字节要读入时,您可以告诉读取 10 个字节。但是,您处于未定义行为的领域,程序可能会按预期“工作”,或者可能发生其他任何事情。fgets

如果您的程序即使在使用这种未定义的行为时也能正常工作,那么您的程序保留的空间可能比 5 个字节多得多,因此写入数组末尾实际上不会导致运行时错误。不要指望这个。

例如,您的代码在我的计算机上编译时,无法逃脱这种命运。

$ cat test.c
#include <stdio.h>
#include <string.h>

int main() {
    char mystring[5];

    fgets(mystring, 10, stdin);

    printf("len: %d", strlen(mystring));
}
$ gcc test.c
test.c: In function ‘main’:
test.c:9:19: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘size_t {aka long unsigned int}’ [-Wformat=]
     printf("len: %d", strlen(mystring));
                  ~^   ~~~~~~~~~~~~~~~~
                  %ld
$ ./a.out
hello world foo bar
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

注意:可以通过对 类型为 的参数使用格式说明符来消除警告。%zusize_t

评论

2赞 Jonathan Leffler 11/6/2023
哪个版本的 GCC 推荐而不是格式(或)?这是一个奇怪的选择,因为它需要一个有符号的长整型,而消息说等同于 .%ld%lu%zusize_tunsigned long
3赞 Harith 11/6/2023 #2

考虑到数组 mystring 的长度设置为 5?

一旦抽象状态机达到未定义的行为,就无法对执行的继续做出进一步的假设。

请参阅:未定义、未指定和实现定义的行为

考虑:

// fgets(mystring, 10, stdin);
fgets(mystring, sizeof mystring, stdin);

旁白:

strlen()返回 a ,而不是 .调用未定义的行为作为格式说明符与相应的参数不匹配。size_tintprintf()

// printf("len: %d", strlen(mystring));
printf("len: %zu", strlen(mystring));

评论

0赞 Chris 11/6/2023
OP 似乎非常清楚,他们不应该提供比缓冲区更大的尺寸,而是询问它为什么“有效”。据推测,提供这种投入是一项学术实验。
0赞 Harith 11/6/2023
@Chris承认。