【问题标题】:for loop brackets in cc中的for循环括号
【发布时间】:2014-10-18 02:45:09
【问题描述】:

我编写了一个程序来使用 for 循环打印整数值,打印后,程序应该等待一秒钟,然后这些整数被双空格字符覆盖,换句话说,程序的目的是在等待后擦除这些整数一秒钟。

这个程序:

#include <stdio.h>
#include <time.h>

int main (void) {
   int i;

   for(i=1;i<=5;i++){
       printf("%d ",i);
   }

   for(;clock () < CLOCKS_PER_SEC;){} /*wait for one second*/

   printf("\r");     /*move to the beginning of the line*/ 
   for(i=1;i<=5;i++){
      printf("  ");   /*overwriting the integers*/
   }
   printf("\n");

   return 0;
}

问题出在等待循环循环括号`for(;clock ()

请有人解释发生了什么?

【问题讨论】:

    标签: c for-loop


    【解决方案1】:

    去掉括号后,printf("\r") 语句就变成了for循环的主体,逻辑上等价于:

    for(;clock () < CLOCKS_PER_SEC;) {printf("\r");}
    

    所以整数会立即被覆盖,而不是在延迟期结束后。

    当然,真正的问题是为什么要使用繁忙循环来延迟,而不是仅仅调用 sleep(1),后者效率更高(即它不会在延迟期间将 CPU 固定在 100%期间)

    【讨论】:

    • 去掉括号会使程序工作,而不是fail,所以这个答案是倒退的;真正的问题是在打印\r 之前不会刷新标准输出。
    • 嗯......好吧,我认为“工作”在这两种情况下都不是一个非常准确的术语,因为程序正忙于等待,因此无论哪种方式都有错误;但是,由于在 for 循环的每次迭代中都执行 printf("\r")(在一秒钟内可能有数万个),许多回车字符的生成有效地通过以下方式刷新标准输出缓冲区蛮力。
    【解决方案2】:

    您不会自己刷新 stdout(printf 写入的流),因此在写入 '\r' 之前不会发生这种情况,然后您会立即清除它。

    如果你删除{},那么你的循环就相当于

    for(;clock () < CLOCKS_PER_SEC;)
        printf("\r");
    

    它写了一堆\r,其中第一个刷新输出,其余的都是多余的。循环完成后,您清除线路,按照您的意愿工作。

    您应该在打印数字后致电fflush(stdout)。或者您可以移动 printf("\r") 使其位于等待循环之前(区别在于光标结束的位置)。

    您的循环有问题,因为不能保证 clock() 从 0 开始,而且它不会在许多系统上运行,而且您不应该那样旋转……它会减慢系统上运行的其他程序的速度。你可以只使用 sleep(1),虽然它不是很准确。

    【讨论】:

      【解决方案3】:

      我怀疑两种情况下输出缓冲区的刷新方式不同。您可以通过在有问题的循环之前使用 fflush(stdout) 手动刷新缓冲区来检查这一点。

      另请注意,对于循环中的单行语句,{} 在 C 中不是强制性的。

      【讨论】:

      • 你能解释一下stdout到底是什么吗?
      • @JeremyFriesner 的回答更加贴切和正确。 stdout 是“标准输出”——本质上是您的屏幕。
      • 杰里米的回答不正确;他倒退了哪个版本有效,哪个无效,并且没有发现问题与不刷新标准输出有关。你对{} 的评论很奇怪;确实,您不必将{ 和} 放在一个语句周围,但仅删除{} 肯定不是无操作。
      【解决方案4】:

      这是您可能想要的代码:

      #include <stdio.h>
      
      int main(int argc, char * argv[]) {
          int seconds = 10;
      
          while(seconds>0) {
              printf("%10d", --seconds);
              fflush(stdout);
              sleep(1);
              printf("\r");
          }
          printf("%10s\n", "time up!");
      
          return 0;
      }
      

      (既然你问 fflush() 实际上是什么,这里根据我的理解做一点解释)

      都是关于 io 缓存,缓存存在的一个原因是:读/写内存可能比硬盘快 1000 倍以上。

      所以程序应该尽量降低读写硬盘的频率,改用内存,但需要对用户体验和io延迟进行适当的权衡。

      例如

      • 逐行读取文件时,一次可以读取2kb左右,而不是单行,然后可以从内存缓存中读取,
      • 当写入控制台时,程序可能会选择写入内存缓存,直到遇到 \n 或 \t 字符,以及其他一些情况。

      fflush(FILE * file),是 stdio.h 中的一个函数,它刷新指定 FILE 的缓存。在您的情况下,该文件是标准输出(标准输出),它会打印到您的控制台。当您使用 printf() 打印单个数字时,它可能会写入 stdout 的缓存,因此您在控制台中看不到它,但调用 fflush(stdout) 会将缓存刷新到您的控制台。


      【讨论】:

      • 我还不了解fflush(stdout)。你能解释一下它到底是什么吗?
      • @harianja 看到我更新的答案,它解释了一点,但要真正理解你需要谷歌和书籍和练习来检查 io 和缓存。
      • @harianja 当您调用 printf 时,它会将数据存储在缓冲区中,而不是立即将其写入文件或终端(这是出于效率原因)。刷新缓冲区会将其写入设备。当缓冲区填满时,或者当您要求使用 fflush 显式刷新它时,或者对于终端,当写入换行符(或者显然是系统上的 '\r')时,就会发生这种情况。
      【解决方案5】:

      在第一个“for”循环中,您使用 printf 打印值。这里'printf'使用'stdout',它是一个缓冲输出——意味着除非提供'\n'或缓冲区已满,否则不会打印输出。因此您可以在第一个循环之后使用flush(stdout),也可以使用fprintf(stderr, "") 打印到不是缓冲输出的标准错误。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-28
        • 1970-01-01
        • 1970-01-01
        • 2022-12-05
        • 1970-01-01
        相关资源
        最近更新 更多