1

我有一个循环运行,每次迭代都会增加一个变量的值,我希望能够按键盘上的一个键来停止循环并报告变量的最终值。问题是,我不知道如何在 C 中做到这一点。我觉得很愚蠢,因为我似乎忽略了一些非常简单易行的方法,但是我尝试的一切都会停止循环,直到我按下一个键键盘,这基本上与我想要的完全相反。

基本上我想做的是这样的:

 
   while (key is not pressed)
increment value
print final value

那有意义吗?无论如何,有关如何在 C 中执行此操作的任何提示?

4

10 回答 10

7

如果您尝试从 linux 中的终端一次读取一个字符(不按 Enter),那么您需要将终端设置为无缓冲输入。

看这个例子:

GNU/Linux 下的非缓冲 getc(3)

于 2009-07-31T23:36:36.387 回答
2

如果您在 Windows 上工作,并且正在使用 MSVC,您可能需要 getch() 和 kbhit(),类似这样

#include <conio.h>

while( looping ) {

    // do regular loop stuff

    // check if a key is hit, w/o blocking, using kbhit()
    if( kbhit() ) {
        // only runs when user has hit a key
        // so display stuff here,
        // and wait for permission to resume with getch()
        getch();
    }
}
于 2009-07-31T23:39:06.787 回答
2

如果您被允许指定按下的内容并且您在符合 POSIX.1 的系统上,您可以设置一个信号处理程序来捕获 SIGINT(由 Ctrl+C 发送)。让您的处理程序更改变量的值,以便您退出 while 循环。

如果您选择这种方法,请小心。如果不正确的实现导致无限循环和捕获的 SIGINT,您将无法通过 Ctrl+C 终止程序。在这种情况下,您需要使用 kill(1) 来终止您的程序。

于 2009-07-31T23:44:47.667 回答
1

这是 C 标准让程序员无所适从的地方。这个问题最便携的解决方案是使用library进行cursesI/O ,它处理所谓的“原始”键盘输入(这是你想要的)以及更多。学习曲线有点陡峭,但有很好的教程,尤其是在 BSD 程序员文档中。

于 2009-08-01T02:02:01.043 回答
1

这取决于您的平台。C 语言没有定义这样的东西。

视窗?linux?(gnome 应用程序?kde 应用程序?终端?)还有什么?

于 2009-07-31T23:30:48.410 回答
0

我将尽量不看你所问的逐字问题,而是看你的意图——显然你想呈现由一些用户反应触发的计算结果。

检查“是否按下了键?” 每次迭代都是相当浪费的——你的 CPU 可以把所有的时间都花在做更有用的事情上。

因此,在我看来,这里最好的方法是使用信号 - 即当您点击“Ctrl-C”时触发的 SIGINT。这是一个代码,当您按下 Ctrl-C 时将打印变量的值,并在您执行 3 次后退出:

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

long globalvar = 0;
int interrupts_before_exit = 3;

void ctrl_c_handler(int x) {
  printf("Value of the variable: %ld\n", globalvar);
  if(--interrupts_before_exit) {
    printf("Press Ctrl-C %d more times to stop\n", interrupts_before_exit);
  } else {
    printf("Computation interrupted!\n");
    exit(0);
  }
}


int main(int argc, char *argv[]) {
  struct sigaction act;

  act.sa_handler = ctrl_c_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  if(sigaction(SIGINT, &act, NULL) >= 0) {
    while (1) {
      /* The work happens here */
      globalvar++; 
    }
  }
  exit(1);
}

您可以在程序之间传递信号,因此实际上您可以分叉执行“工作”的程序,然后第二个程序将懒惰地监视键盘-一旦在那里按下键,它就会将信号发送到第一个,它将打印结果。

于 2009-08-01T13:04:24.933 回答
0

您为读取字符而调用的函数正在阻塞。如果那里没有字符,您希望它返回。

这里的魔术搜索词是非阻塞和非/无缓冲输入。

于 2009-07-31T23:31:11.140 回答
0

为什么要循环计数直到有人按下键才有意义?

如果你真的想要类似的东西而不是检查像 10k 迭代的时间。然后在睡眠()的非阻塞迭代中等待。当你有时间时,你可以用它来近似“最终值”是什么

于 2009-07-31T23:34:51.303 回答
0

根据系统及其工作方式,您可以尝试将 select 函数与 STDIN 一起用作文件句柄。您可以将 select 语句上的时间设置为零以轮询以查看是否有数据或将其设置为等待时间。

你可以看看链接http://www.gnu.org/s/libc/manual/html_node/Waiting-for-I_002fO.html使用带有套接字的 select 语句的示例。

我已修改该示例以使用 STDIN 作为文件描述符。如果没有挂起的输入,该函数将返回 0,如果有挂起的输入(即有人按下输入键盘上的键),则返回 1,如果出现某种性质的错误,则返回 -1

 #include <errno.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/time.h>

 int waitForKBInput(unsigned int seconds)
 {
   /* File descriptor set on which to wait */
   fd_set set;
   /* time structure which indicate the amount of time to
      wait.  0 will perform a poll */
   struct timeval timeout;

   /* Initialize the file descriptor set. */
   FD_ZERO (&set);
   /* Use the Standard Input as the descriptor on which 
      to wait */
   FD_SET (STDIN, &set);

   /* Initialize the timeout data structure. */
   timeout.tv_sec = seconds;
   timeout.tv_usec = 0;

   /* select returns 0 if timeout, 1 if input available, -1 if error. */
   /* and is only waiting on the input selection */
   return select (FD_SETSIZE,
                  &set, NULL, NULL,
                  &timeout));
 }

我知道这在 VMS 系统上不起作用,因为我尝试了这个并且他们以不同的方式实现了 Select 和 STDIN,因此它不起作用(必须使用其他方法来检测键盘输入)。

对于 Visual C/C++ 可以使用函数 kbhit 来指示是否有要读取的键盘输入。

于 2009-08-01T03:08:10.290 回答
0

有些人提到了阻塞。在 *nix 环境中,您可以尝试将标准输入文件描述符设置为非阻塞模式。这是一个例子:

/*  testbrk

  Test breaking an infinite loop with a keystroke.
  Hit the <Enter> key to break this loop.

*/


/* Include block */
#include  <errno.h>
#include  <fcntl.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>


/*---testbrk main loop---*/

int  main ( int argc , char ** argv )
{

  /* Variables: file flags , return value , buffer */
  int  f ;
  ssize_t  r ;
  char  b ;

  /*
    Switch standard input to non-blocking mode:
    -Get existing flags from the file descriptor
    -Bitwise OR to activate non-blocking bit
    -Apply new flags
  */
  if  ( f = fcntl( STDIN_FILENO , F_GETFL , 0 )  ==  -1 )
  {
    perror ( "fcntl F_GETFL" ) ;
    exit ( EXIT_FAILURE ) ;
  }

  f |= O_NONBLOCK ;

  if  ( f = fcntl( STDIN_FILENO , F_SETFL , f )  ==  -1 )
  {
    perror ( "fcntl F_SETFL" ) ;
    exit ( EXIT_FAILURE ) ;
  }


  /*
    Infinite loop
    -try reading from standard input directly via its file
     descriptor , NOT the standard input stream
    -if the number of bytes returned is > zero then break
    -if bytes returned is -1 then check errno for EAGAIN,
     meaning that read would block but the file is nonblocking
    -if zero is returned then print message immediately
  */
  while  ( (r = read( STDIN_FILENO , &b , sizeof( b ) ))  <  1 )
  { 

    if  ( r == -1  &&  errno != EAGAIN )
    {
      perror ( "read" ) ;
      exit ( EXIT_FAILURE ) ;
    }

    fprintf ( stderr , "\nInfinite loop" ) ;

  } /* inf loop */

  /* Done */
  printf ( "\n\nBroken loop\n\n" ) ;
  exit ( EXIT_SUCCESS ) ;

} /* testbrk */
于 2017-11-24T16:38:14.193 回答