【问题标题】:How to optimize C code with else if conditions如何使用 else if 条件优化 C 代码
【发布时间】:2022-01-09 19:16:13
【问题描述】:

我正在学习一些 C 的基础知识(我实际上是在做哈佛的 cs50x)并且我编写了以下代码:

#include <cs50.h>
#include <stdio.h>

int main(void) {
    while(0 == 0) {
        printf("1. SUM\n2. SUBSTRACTION\n3. MULTIPLICATION\n4. DIVISION\n5. QUIT\n");
    
        int a = get_int("Choose one option: ");
        
        if (a == 1) {
            int x = get_int("x: ");
            int y = get_int("y: ");
            int sum = x + y;
            int sub = x - y;
            int division = x/y;
            int mul = x*y;
            printf("Result = %i\n\n", sum);
        }
        else if (a == 2) {
            int x = get_int("x: ");
            int y = get_int("y: ");
            int sum = x + y;
            int sub = x - y;
            int division = x/y;
            int mul = x*y;
            printf("Result = %i\n\n", sub);
        }
        else if (a == 3) {
            int x = get_int("x: ");
            int y = get_int("y: ");
            int sum = x + y;
            int sub = x - y;
            int division = x/y;
            int mul = x*y;
            printf("Result = %i\n\n", mul);
        }
        else if (a == 4) {
            int x = get_int("x: ");
            int y = get_int("y: ");
            int sum = x + y;
            int sub = x - y;
            int division = x/y;
            int mul = x*y;
            printf("Result = %i\n\n", division);
        }
        else if (a == 5) {
            printf("Alright! See you soon!\n");
            break;
        }
        else {
            printf("Invalid Input\n");
            break;
        }
    }
}

它完全按照我的意愿工作,但我觉得它可以用更好的方式编写。
我真的不喜欢 int 变量的所有重复,我认为它可以以某种方式进行优化,但不知道如何。

【问题讨论】:

  • 只需使用while(1),因为在C 中任何非0 整数值都是trueif...else 的链通常由switch() 和各种case 语句完成,最后的elsedefault:
  • 但无论如何我总是需要写所有那些“int x = get...”、“in y = ...”、“int sum = ...”等...每一个时间?
  • 你为什么要在所有if 分支中重复算术计算,即使它们不需要?
  • 我认为这样会更好,但实际上 LucaRicardo 的做法是最好的方法

标签: c optimization cs50


【解决方案1】:

除了“QUIT”和“invalid”操作之外的所有操作都有行

int x = get_int("x: ");
int y = get_int("y: ");

共同点。因此,将这两行移到if...else if 链之外是有意义的,这样您只需编写一次。但是“QUIT”和“invalid”的情况必须事先处理好,这样用户想退出或者输入无效时,都不会提示输入这两个操作数。

此外,您始终执行所有 4 种算术运算(加法、减法、乘法和除法),尽管只需要计算用户选择的那一项。

另外,我怀疑这些行是否正确:

else {
    printf("Invalid Input\n");
    break;
}

如果用户输入了无效的输入,您可能希望通过重新启动循环来重新提示用户输入,而不是退出程序。

您可以使用switch 语句,而不是使用长的if...else if 链。

线

while (0==0){

在技术上是正确的,但是这样写是非常少见的。

为了创建无限循环,通常会写成while (1)for (;;),后者是更传统的语法。

这是一个包含上述改进的程序:

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

int main( void )
{
    for (;;)
    {
        printf(
            "1. SUM\n"
            "2. SUBSTRACTION\n"
            "3. MULTIPLICATION\n"
            "4. DIVISION\n"
            "5. QUIT\n"
        );
    
        int a = get_int( "Choose one option: " );

        if ( a == 5 )
        {
            printf( "Alright! See you soon!\n" );

            //break out of infinite loop
            break;
        }

        //verify that input is between 1 and 4
        if ( ! ( 1 <= a && a <= 4 ) )
        {
            printf( "Invalid Input\n\n" );

            //restart loop so that user gets reprompted for input
            continue;
        }

        int x = get_int( "x: " );
        int y = get_int( "y: " );

        switch ( a )
        {
            case 1:
                printf( "Result = %i\n\n", x + y );
                break;
            case 2:
                printf( "Result = %i\n\n", x - y );
                break;
            case 3:
                printf( "Result = %i\n\n", x * y );
                break;
            case 4:
                printf( "Result = %i\n\n", x / y );
                break;
            default:
                //these lines should never be reached
                fprintf( stderr, "internal program error!" );
                exit( EXIT_FAILURE );
        }
    }
}

【讨论】:

    【解决方案2】:

    @AndreasWenzel 的回答已经很不错了,使用了条件保护和控制流工具,比如continuebreak

    作为替代方案,您可以分配给开关中的result 变量,然后执行单个printf 进行输出。

    int main(void) {
        while (1) {
            printf("1. SUM\n2. SUBSTRACTION\n3. MULTIPLICATION\n4. DIVISION\n5. QUIT\n");
        
            int a = get_int("Choose one option: ");
            
            if (a == 5) {
                printf("Alright! See you soon!\n");
                return 0;
            } 
            else if (a < 1 || a > 4) {
                printf("Invalid Input\n");
                continue;
            }
    
            int x = get_int("x: ");
            int y = get_int("y: ");
            int result;
    
            switch (a) {
                case 1:
                result = x + y;
                break;
    
                case 2:
                result = x - y;
                break;
    
                case 3:
                result = x * y;
                break;
     
                case 4:
                result = x / y;
                break;
            }
    
            printf("Result = %i\n", result);
        }
    
        return 0;
    }
    

    作为替代方案,我们可以像以前一样检查良好的输入和退出选择,但使用函数指针数组来非常简洁地实现这一点。

    int add(int a, int b) { return a + b; }
    int sub(int a, int b) { return a - b; }
    int mul(int a, int b) { return a * b; }
    int div(int a, int b) { return a / b; }
    
    typedef int(*op)(int, int);
    
    int main(void) {
        op ops[] = { add, sub, mul, div };    
    
        while(0 == 0) {
            printf("1. SUM\n2. SUBSTRACTION\n3. MULTIPLICATION\n4. DIVISION\n5. QUIT\n");
        
            int a = get_int("Choose one option: ");       
    
            if (a == 5) {
                printf("Alright! See you soon!\n");
                return 0;
            } 
            else if (a < 1 || a > 4) {
                printf("Invalid Input\n");
                continue;
            }
    
            int x = get_int("x: ");
            int y = get_int("y: ");
            int result = ops[a - 1](x, y);
    
            printf("Result = %i\n", result);
        }
    
        return 0;
    }
    

    更进一步,我们可以将要执行的操作的选择分解为具有所有必需输入错误处理的单独函数,并将函数指针和菜单选项分组到结构中。

    typedef int(*op)(int, int);
    
    typedef struct {
        op operation;
        char *menu_option;
    } option;
    
    option get_option(char *prompt, char * out_of_range_msg, char *out_of_tries_msg, 
                      option *ops, size_t n, int tries,
                      int add_quit_option, char *quit_option_text, char *quit_msg) {
        while (tries--) {
            for (size_t i = 0; i < n; ++i) {
                printf("%2d: %s\n", i + 1, ops[i].menu_option);
            } 
    
            if (add_quit_option) {
                printf("%2d: %s\n", n + 1, quit_option_text);
            }
    
            int opt = get_int(prompt);
    
            if (opt < 1 || opt > (add_quit_option ? n + 1 : n)) {
                printf("%s\n", out_of_range_msg);
                continue;
            }
    
            if (add_quit_option && opt == n + 1) {
                printf("%s\n", quit_msg);
                exit(EXIT_SUCCESS);
            }
    
            return ops[opt - 1];
        }
    
        printf(out_of_tries_msg);
        exit(EXIT_FAILURE);
    }
    

    现在main 变得更加简洁了,我们看到添加额外的操作变得多么容易。

    int main(void) {
        option ops[] = { 
            {addop, "Sum"}, {subop, "Subtraction"}, 
            {mulop, "Multiplication"}, {divop, "Division"}, 
            {modop, "Modulo"}
        };    
    
        while (1) {
            option op = get_option("Choose One option: ", "Invalid input", "Too many mistakes!", 
                                   ops, 5, 10,
                                   1, "Quit", "Alright! See you soon!");
    
            int x = get_int("x: ");
            int y = get_int("y: ");
         
            printf("Result: %i\n", op.operation(x, y));
        }
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      在每种情况下计算所有结果是没有用的。包含结果的变量也毫无用处,因为您可以在 printf 函数中进行计算,如下所示:

      printf("Result = %i\n\n", x+y);
      

      最后,您可以将 if 语句更改为 switch case,这将使代码看起来更简洁,在性能方面,switch case 部分不会产生任何影响,但其他更改会。

      就像 Weather Vane 评论的那样,你可以这样做 while(1)

      【讨论】:

      • 但是a == 5 不需要这些输入。
      • 我知道,但它仍然会大大缩短这段代码,而且在性能方面它不会产生太大的影响,因为这段代码太短了。
      • 要求用户输入两个操作数,虽然它们不是退出程序所必需的,但似乎不合适。
      • 从答案中删除了,我猜我在写答案的时候有点困。
      • @LucaRicardo:您将这两行移出if...else if 链的想法实际上很好,但是如果您这样做,则必须处理“QUIT”的情况并事先“无效”。请参阅我的答案以获取执行此操作的解决方案。
      【解决方案4】:

      这是完整的代码版本

      #include <stdio.h>
      #include <stdlib.h>
      
      int main() {
      int choose = 0, num1, num2, work;
      float div;
      
      while(choose!=5){
          
          printf(" 1. SUM\n 2. SUBSTRACTION\n 3. MULTIPLICATION\n 4. DIVISION\n 5. QUIT\n");
          printf("Choose an option... \n");
          scanf("%d", &choose);
      
          printf("\nInsert two numbers: \n");
          scanf("%d", &num1);
          scanf("%d", &num2);
          
          switch(choose){
              case 1:
                  work = num1 + num2;
                  printf("Sum: %d", work);
                  break; //you can use it to terminate your case switch instruction
              
              case 2:
                  work = num1 - num2;
                  printf("Sub: %d", work);
                  break;
      
              case 3:
                  work = num1 * num2;
                  printf("Multiplication: %d", work);
                  break;
      
              case 4:
                  div = num1 / num2;
                  printf("Division: %f", div);
                  break;
              
              case 5:
                  choose = 5;
                  break;
              
              default:
                  printf("Error / invalid input");
                  break;
          }
      }
      

      }

      【讨论】:

      • 请注意,您可以编辑现有答案以添加其他信息或代码,而不是发布新答案。
      • 这个程序有一个bug:如果用户输入5("QUIT")或者输入不在15的范围内,那么它不适合这个程序提示用户输入两个操作数值。
      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案5】:

      要推进您的代码,您可以创建操作函数,然后将它们与 if...else ladder 或 switch...case 语句一起使用。您可以查看以下代码并提出任何问题。请注意,我不是专业的 C 程序员,这就是我修改您的代码的方式。我相信会有更好的方法来修改你的程序。

      #include <stdio.h>
      
      int main(void) {
          
              int a;
          
              float x;
              float y;
              
              float result;
              float my_remainder;
              
              // Let's create functions first
              float my_sum(float x, float y)        // function for addition
              {
                  result = x + y;
              };
              
              float my_sub(int x, int y)           // function for subtraction
              {
                  result = x - y;
              };
              
              float my_mul(int x, int y)           // function for multiplication
              {
                  result = x * y;
              };
              
              float my_div(int x, int y)          // function for divison
              {
                  result = x / y;
                  my_remainder = x % y;
              };
              
          while(1){
              start:
              printf("1. SUM\n2. SUBSTRACTION\n3. MULTIPLICATION\n4. DIVISION\n5. QUIT\n");
              
              choose:                     // let's choose an option, but can choose only one of the mentioned ones
              printf("choose one option: ");
              scanf("%d", &a);
              
              switch(a)                  // check if "a" is not equal to either one of the mentioned numbers, then go back to 'choose' to ask for the right one
              {
                  case 1: case 2: case 3: case 4: case 5: goto letsgo;
                  
                  default:
                  printf("Invalid Input. Please choose an option from the given ones:\n\n"); 
                  goto start;
              };
              letsgo:
              if(a == 5)                  // firstly, need to check QUIT condition, if QUIT is selected then the while loop stops (the process ends)
              {
                  printf("\nAlright! See you soon!\n");
                  //return 0;
                  break;
              }
              
              else                        // if QUIT is not selected, it means one of the operations should be done
              {                           // so let's get the numbers for corresponding calculation
              printf("Please enter first number: ");
              scanf("%f", &x);
              printf("Please enter second number: ");
              scanf("%f", &y);
              
              if(a == 1)          // addition
              {
                  my_sum(x,y);
                  printf("Result = %.2f", result);        // %.2f <-- "2" represents the number of digits after the decimal separator. 
                 return 0;                                // e.g., 1.4 + 1.2 = 2.60, however, if it was %.4f, then: 1.4 + 1.2 = 2.6000
              }
              else if(a == 2)     // subtraction
              {
                  my_sub(x,y);
                  printf("Result %.2f", result);
                  return 0;
              }
              else if(a == 3)     // multiplication
              {
                  my_mul(x,y);
                  printf("Result %.2f", result);
                  return 0;
              }
              else if(a == 4)     // division
              {
                  my_div(x,y);
                  printf("Result %.2f", result);
                  if(my_remainder != 0)           // check if remainder is not equal to 0, then print, otherwise, don't print
                  {
                  printf("\nRemainder: %.2f", my_remainder);
                  }
                  return 0;
              }
              
              }
              
          }
              
      }
      

      【讨论】:

      • 你工作太辛苦了。开关盒脱落。你只需要case 1: case 2: case 3: case 4: case 5: goto letsgo;如果你想追求这种流量控制。
      • 嗨@Chris。我绝对同意您的看法,感谢您对我修改后的代码的关注!当我写这段代码时,我特别想缩短那部分。这是我在这里的第一次体验,因为有来自世界各地的像您这样的贡献者,我已经很高兴了!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 2017-09-19
      • 1970-01-01
      • 2022-11-16
      • 1970-01-01
      • 1970-01-01
      • 2017-02-27
      相关资源
      最近更新 更多