C中断程序流程

时间:2009-07-31 23:17:57

标签: c keyboard program-flow

我有一个循环运行,每次迭代都会增加一个变量的值,我希望能够点击键盘上的一个键来停止循环并报告变量的最终值。事情是,我无法弄清楚如何在C中做到这一点。我感到愚蠢,因为我似乎忽略了一些非常简单易行的方法,但我尝试的一切都会停止循环直到

基本上我想要做的是这样的事情:

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

这有意义吗?无论如何,有关如何在C中做到这一点的任何提示?

11 个答案:

答案 0 :(得分:7)

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

见这个例子:

Non buffered getc(3) under GNU/Linux

答案 1 :(得分: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();
    }
}

答案 2 :(得分:2)

如果允许您指定按下的内容,并且您在符合POSIX.1的系统上,则可以设置信号处理程序以捕获SIGINT(通过Ctrl + C发送)。让处理程序更改变量的值,使其脱离while循环。

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

答案 3 :(得分:2)

我认为没有简单的解决方案,请看一下这个相关的问题

  

detect keyboard event in C

答案 4 :(得分:1)

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

窗? Linux呢? (gnome app?kde app?terminal?)还有什么?

答案 5 :(得分:0)

您正在调用的用于读取角色的功能正在阻止。如果那里没有角色,你想要它返回。

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

答案 6 :(得分:0)

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

如果你真的想要的东西比检查10k iterrations的时间要好。 然后在非阻塞itteration whit sleep()中等待。当你得到时间时,你可以用它来估算“最终值”是什么

答案 7 :(得分:0)

这是C标准让程序员闲逛干燥的地方。解决此问题的最便携的解决方案是使用 curses来处理您的I / O,它处理所谓的“原始”键盘输入(这是您想要的)加上一个整体更多。学习曲线有点陡峭,但有很好的教程,特别是在BSD程序员的文档中。

答案 8 :(得分:0)

根据系统及其工作原理,您可以尝试将STDIN作为文件句柄使用select函数。您可以将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来指示是否有要读取的键盘输入。

答案 9 :(得分:0)

我会试着不要看你要问的逐字问题,而是按照你的意图 - 显然你想要呈现由一些用户反应引发的计算结果。

检查'是按键?'在每次迭代都相当浪费 - 你的CPU可以花费所有时间做更多有用的事情。

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

#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);
}

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

答案 10 :(得分: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 */