提问人:Tom_the_cat 提问时间:7/1/2018 最后编辑:alkTom_the_cat 更新时间:7/1/2018 访问量:987
如何正确使用EOF?
How to properly using EOF?
问:
我对EOF有疑问。
首先,我正在编写一个简单的程序,用于复制/打印用户的输入。
但是,程序也会在输出中复制 EOF。
例如,我的操作系统是 Window,当我按顺序输入(Enter -> cntrl + z -> Enter)时,我的 EOF 有效。如果我输入“Hello”+Enter + EOF组合键,输出会在复制的用户输入末尾打印奇怪的字母('?')。
我怎样才能摆脱输出末尾的“?”,为什么会这样?
#include <stdio.h>
void copy(char to[], char from[]);
main()
{
int i;
int c;
char origin[10];
char copied[10];
for(i = 0; (c = getchar()) != EOF; ++i)
{
origin[i] = c;
}
copy(copied, origin);
for(i = 0; i < 10; i++)
putchar(copied[i]);
}
void copy(char to[], char from[])
{
int i;
i = 0;
while((to[i] = from[i]) != '\0')
i++;
}
答:
4赞
Spikatrix
7/1/2018
#1
您忘记了 NUL 终止 .因此,您可以在复制过程中调用 Undefined Behavior。改用以下代码获取输入:origin
for(i = 0; i < 9 && (c = getchar()) != EOF; ++i) /* `i < 9` to prevent array overruns */
{
origin[i] = c;
}
origin[i] = '\0'; /* NUL-terminate your string */
同时将打印代码更改为:
for(i = 0; copied[i] != '\0'; i++) /* Print until a NUL-terminator */
putchar(copied[i]);
评论
2赞
Basile Starynkevitch
7/1/2018
不,这是正确答案。默认情况下,本地数组没有正确初始化(因此程序员应该考虑初始化它)
1赞
Basile Starynkevitch
7/1/2018
该语句正确地以 null 结尾的字符串origin[i] = '\0';
origin
0赞
Yunnosch
7/1/2018
@BasileStarynkevitch 我需要三次重复才能看到 0 被复制。谢谢。
1赞
Yunnosch
7/1/2018
#2
您将无条件地输出数组的所有 10 个成员。
您可以通过将字母末尾经常使用的字母附加到输出中来修复。
使用'\0'
origin[i] = '\0';
读完后。
最后输出到那个标记,而不是所有东西
for(i = 0; copied[i]!='\0'; i++)
这样可以保持您的假设,即数组足够大以保留输入(包括添加的 )。但是,您应该防止这种情况,例如,通过对任何循环使用双重条件,检查是否访问允许的最高数组索引。'\0'
评论
0赞
David C. Rankin
7/1/2018
可能还想评论一下有多危险......的输入可能非常糟糕。for(i = 0; (c = getchar()) != EOF; ++i)
"Hello Newb<nasty shellcode>"
0赞
ChandraKumar
7/1/2018
#3
您正在使用 IDE(可能是 CodeBlocks),它在后续 IO 操作之间使用页面缓冲区,这就是您实际获得输出的原因。
接下来,您将强制在输出 for 循环中打印数组的所有十个元素,这是糟糕的编码实践。
这个简单的片段可以帮到你
scanf("%10[^\n]s",input);
使用它从文件 ./youpro < file_name_where_to_fetch_input读取输入
感谢 David C. Rankin 在评论中提到这个错误。
评论
0赞
Yunnosch
7/1/2018
我认为 OP 对他们如何输入的描述是合理的。EOF
0赞
ChandraKumar
7/1/2018
也许,确实如此。我不是来自Windows背景...我的答案更倾向于 Linux,一般来说,当从终端编译时......你不能做这些神奇的组合来进入EOF......所以,应该遵循一个std做法,就像我的回答一样。 我在 Windows 上可能错了。.
1赞
David C. Rankin
7/1/2018
“无法输入'EOF'作为输入”??当然有。 在 Linux 上生成一个手册,并在 windoze 上做同样的事情。但请参阅:CTRL+Z 不会在 Windows 10 中生成 EOFCtrl+d
EOF
Ctrl+z
1赞
David C. Rankin
7/1/2018
关于对 IDE 的猜测,您仍然处于一个瘦弱的分支上(无论如何这并不重要),但良好的第一次努力。请记住,在 StackOverflow 上回答时,您进入了 Teacher 的名册。你要确保你是好人之一,而不是比我们开始上课时更让我们感到困惑的老师之一。保持良好的努力,并始终努力做到彻底和正确。
2赞
chqrlie
7/1/2018
字符类的转换说明符没有尾随符,指定的数字必须比数组的大小小 1。它应该写.此外,如果用户输入空行,这将失败,如果忽略返回值 ,则会导致未定义的行为。s
scanf("%9[^\n]", origin);
scanf()
scanf()
3赞
chqrlie
7/1/2018
#4
该问题与代码中存在多个问题完全无关,导致潜在的未定义行为和不必要的副作用:EOF
- 读取循环一直持续到文件末尾:如果输入流长度超过 10 个字节,则代码将导致缓冲区溢出,将字节存储在数组末尾之外。这是未定义行为的第一个案例。
origin
- 本地数组未初始化,因此其内容不确定。您不会在从 读取的字节之后将 null 终止符存储到其中。
origin
stdin
- 在函数中,您依靠 null 终止符来停止复制循环,但由于没有存储任何内容,因此在复制所有从中读取的字节后,您可以访问未初始化的内容。空终止符检验与 中的赋值相结合。访问未初始化的数据具有未定义的行为。此外,您一直读取,直到找到 null 终止符,如果最终读取超过数组末尾,则会导致进一步的未定义行为,而在数组末尾之后写入时更是如此。
copy
stdin
while((to[i] = from[i]) != '\0')
origin
copied
- 最后一个循环输出数组的所有 10 个元素。
copied
- 即使数组的末尾可能偶然包含空字节,从而防止函数中出现未定义的行为。输出循环仍会输出有趣的字符,因为您不会停在 null 终止符处,而是将其打印到 ,并且当您在此之后读取未初始化的内容时,再次具有未定义的行为。
origin
copy
stdout
copied
- 另请注意,without arguments 的原型是 。您使用的语法(没有返回类型)在 70 年代和 80 年代很常见,但现在已经过时,不应再使用。
main
int main(void)
以下是更正后的版本:
#include <stdio.h>
void copy(char to[], char from[]);
int main(void) {
int i;
int c;
char origin[10];
char copied[10];
for (i = 0; i < 10 - 1 && (c = getchar()) != EOF; i++) {
origin[i] = c;
}
origin[i] = '\0';
copy(copied, origin);
for (i = 0; copied[i] != '\0'; i++) {
putchar(copied[i]);
}
return 0;
}
void copy(char to[], char from[]) {
int i;
i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}
评论
int main(void)
main()
EOF
不是类型的值(例如,在 -s 为 的计算机上,可能是 -1)。所以你不能复制,根据定义!char
char
unsigned
EOF
EOF