【问题标题】:If statement inside while loop with the same condition条件相同的while循环中的if语句
【发布时间】:2019-04-04 13:27:03
【问题描述】:

有没有更好的方法通过消除C语言中if语句中的重复条件来编写以下代码?

while (n < 0) {
   printf("Enter a positive integer: ");
   scanf("%d", &n);

   if (n < 0) {
      printf("Error: please enter a positive integer\n");
   }
}

谢谢。

【问题讨论】:

  • “更好”是什么意思?更安全?快点?更具可读性?还有,n的类型是什么?
  • 检查scanf的返回值会更好,一方面。
  • n &gt;= 0 可以在 while 循环之前使用吗?如果没有,你可以把它变成一个“无限”循环,并在if 块内添加一个break
  • n 是一个整数。 “更好”是指消除 if 语句中的重复条件。
  • 开启优化后,一个体面的编译器应该优化掉重复的条件,所以这可能不值得担心。消除重复条件的唯一其他方法是跳出无限循环。

标签: c if-statement while-loop conditional-statements


【解决方案1】:

在给出正确的输入后,只需重新设计你的循环中断即可。这样检查只进行一次:

while (1)
{
   printf("Enter a positive integer: ");
   scanf("%d", &n);

   if (n >= 0)
       break;

   printf("Error: please enter a positive integer\n");
}

并且,正如 cmets 中所指定的,优化的编译器应该能够自行反转循环。

【讨论】:

    【解决方案2】:

    这是 IMO 最好通过一些重构来完成的事情:

    #include <stdio.h>
    #include <stdbool.h>
    
    static bool get_postive_integer(int *pOut) {
        int n;
        printf("Enter a positive integer: ");
        scanf("%d", &n);
    
        if(n < 0)
            return false;
    
        *pOut = n;
        return true;
    }
    
    int main(void)
    {
       int n;
       while (!get_postive_integer(&n)) {
           printf("Error: please enter a positive integer\n");
       }
    }
    

    给操作一个名称,检查它是否失败,然后才相应地打印一条消息。成功或失败条件在此处仅在命名操作中编码一次。

    【讨论】:

      【解决方案3】:

      你可以使用:

      while (printf("Enter a positive integer: ") > 0 &&
             scanf("%d", &n) == 1 &&
             n < 0)
      {
          printf("Error: please enter a positive integer\n");
      }
      

      如果printf() 失败,如果scanf() 失败,或者n 中的值是非负数,这将停止。始终检查scanf() 是否成功是个好主意。 printf() 返回它写入的字符数(或失败时为负数)只是方便,因此它也可以在条件下使用。您也可以将fflush(stdout) == 0 &amp;&amp; 添加到操作堆栈中。

      或者你可以决定条件中的代码应该在一个函数中:

      static int read_positive_integer(void)
      {
          int value;
          if (printf("Enter a positive integer: ") > 0 &&
              fflush(stdout) == 0 &&
              scanf("%d", &value) == 1 &&
              value >= 0)
              return value;
         return -1;
      }
      

      然后调用代码是:

      while ((n = read_positive_integer()) < 0)
          printf("Error: please enter a positive integer\n");
      

      主题有很多变化;您可以将while 循环包装到一个函数中;您可以将提示变成函数的参数。您可能决定在报告出现问题时更加小心(如果printf() 失败,则与scanf() 返回 0(输入中的非数字数据)或 EOF(输入中没有更多数据)时发生的情况相比,会发生不同的操作。

      【讨论】:

        【解决方案4】:

        以下示例是本着人们应该知道该语言中可用内容的精神提供的。1我通常编写代码的方式显示在Frankie_C’s answer中。正如一些人所指出的,优化通常使这种简单的情况不值得担心,但问题不仅限于像n &lt; 0 这样的简单评估;测试可能是对更复杂标准的一些昂贵评估的函数调用。

        人们不会喜欢这样,但是:

            goto middle;
            do
            {
                printf("Error, please enter a positive integer\n");
        middle:
                printf("Enter a positive integer: ");
                scanf("%d", &n);
            } while (n < 0);
        

        如果你强烈反对goto,你可以使用Duff’s device的精简版:

            switch (0)
            do
            {
                printf("Error, please enter a positive integer\n");
            case 0:
                printf("Enter a positive integer: ");
                scanf("%d", &n);
            } while (n < 0);
        

        但你不应该。

        脚注

        1 通常,软件工程师必须使用其他人编写的代码,因此他们必须准备好识别和理解该语言中可表达的任何内容,即使这只是重写的第一步它变成更好的代码。有时会出现出于商业或其他实际原因需要“丑陋”代码的情况。

        【讨论】:

        • 已更新。我相当频繁地使用第二种方式(尽管我的格式略有不同;我在一行上写了switch (0) do {。):我不知道你为什么说你不应该使用它。
        【解决方案5】:

        另一种选择是拆分成一个函数:

        int func(){
           int n;
           printf("Enter a positive integer: ");
           scanf("%d", &n);
           return scanf("%d", &n) == 1 ? n : -1;
        }
        

        然后循环变成了

        while ((n = func()) < 0){
            printf("Error: please enter a positive integer\n");
        }
        

        虽然条件检查中的赋值并不符合每个人的口味。请注意,如果 scanf 的返回值不是 1,我会返回 -1,您应该经常检查。


        在这种情况下我所做的(参见 Eric 的回答)是写

        switch (0) do {
            printf("Error, please enter a positive integer\n");
        case 0:
            printf("Enter a positive integer: ");
            scanf("%d", &n);
        } while (n/*ToDo - make sure n is initialised if scanf fails*/ < 0);
        

        【讨论】:

        • 你知道... Eric 对使用goto 很诚实。这里只是打着结构化的幌子偷偷溜进去。
        • @StoryTeller:goto 的问题与它将标签泄漏到翻译单元中的事实与其运行时效果一样重要。我这样做的方式绕过了前者。而switch (0) do 是一些时髦成语的明确信号。
        • 不进翻译单元,进函数。标签具有函数作用域,因此不会在全局范围内泄漏。
        • @StoryTeller:在某些方面我很高兴我不知道这一点。一直都是这样吗?我在大学时使用了非常早期版本的 C。
        • 似乎和C89 一样古老。不过,我不知道 K&R 中是否是这样的。
        猜你喜欢
        • 2020-04-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-23
        • 2015-05-19
        • 2013-11-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多