【问题标题】:Interrupting program flow in C在 C 中中断程序流
【发布时间】:2018-08-05 12:00:02
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这是做家庭作业吗(听起来像我)?
  • 另外,您的实现取决于平台,因为检测按键来自操作系统特定的函数调用。如果您能指出您正在开发的操作系统,那就太好了。

标签: c keyboard program-flow


【解决方案1】:

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

看这个例子:

Non buffered getc(3) under GNU/Linux

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      如果你在 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();
          }
      }
      

      【讨论】:

        【解决方案4】:

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

        窗户? linux? (gnome 应用?kde 应用?终端?)还有什么?

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            【解决方案6】:

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

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

            【讨论】:

              【解决方案7】:

              为什么在有人按下键之前循环计数有意义?

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

              【讨论】:

                【解决方案8】:

                根据系统及其工作方式,您可以尝试将 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 来指示是否有要读取的键盘输入。

                【讨论】:

                  【解决方案9】:

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

                  检查“是否按下了键?”每次迭代都是相当浪费的——你的 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);
                  }
                  

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

                  【讨论】:

                    【解决方案10】:

                    有些人提到了屏蔽。在 *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 */
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2021-08-06
                      • 1970-01-01
                      • 2012-08-18
                      相关资源
                      最近更新 更多