控制台I / O:未按预期顺序出现printf和scanf

问题描述

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

void eat() // clears stdin upto and including \n OR EOF
{
    int eat;while ((eat = getchar()) != '\n' && eat != EOF);
}

int main(){

    printf("\n COMMAND : "); char cmd[21]=""; scanf("%20s",cmd);

    if(strcmp(cmd,"shell")==0||strcmp(cmd,"sh")==0)
    {
        getchar(); // absorb whitespace char separating 'shell' and the command,say 'ls'
        while(1)
        {
            printf("\n sh >>> "); // print prompt
            char shellcmd[1024]=""; // str to store command
            scanf("%1023[^\n]",shellcmd); eat(); // take input of command and clear stdin

            if(strcmp("close",shellcmd)==0||strcmp("x",shellcmd)==0)
                break;
            else
                system(shellcmd);
        }
    }
}

代码中,发生了一些我无法捕捉到的异常行为。

输入sh ls并按[ENTER]后,预期响应为:

  1. 一个scanf()sh存储在cmd[]中,并将 ls\n保留在stdin中。
  2. getchar()占用 空间。
  3. printf()\n sh >>>打印到终端机
  4. 第二scanf()ls存储在shellcmd[]中,将\n保留在stdin中
  5. eat()从标准输入读取\n,将其留空
  6. system("ls")被执行

即结果应该是这样的:

 COMMAND : sh ls

 sh >>>
 file1 file 2 file3 ...

 sh >>> | (cursor)

但是

我得到的是

COMMAND : sh ls

file1 file2 file3 ...
 sh >>> 
 sh >>> | 

显然,第二个scanf()shell()正在之前 printf()执行,或者至少是我的假设。 >

什么不对?

使用cc -Wall -Wextra -pedantic在Clang和GCC上进行了编译,并在MacOS和Linux上的bash上进行了测试

解决方法

man page中可以找到:

如果流指向终端(如stdout通常那样),则它是行缓冲的

因此,当printf所打印的消息不包含换行符时,您可能会遇到延迟。另一方面,发送下一个printf的前导换行符后,就会显示previos消息。

解决方案:

  1. 在您的消息printf("\n sh >>> \n");

    的末尾添加换行符
  2. 通过调用flush()函数(fflush(stdout))强制即使没有换行符也要显示当前缓冲区

  3. 使用setvbuf()函数更改当前的 stdout 缓冲行为

    setvbuf(stdout,NULL,_IONBF,0);