📜  C语言中的基本代码优化

📅  最后修改于: 2021-05-25 20:43:19             🧑  作者: Mango

通常,资源在不同进程之间共享。假设您的程序占用更多资源,那么肯定会影响需要相同资源的其他进程的性能。因此,我们需要考虑资源(例如处理器的时间和主内存)来编写和优化程序。
以下是一些优化技术

  1. 循环优化
    • 展开小循环:大多数情况下,编译器会自动执行此操作,但这是编写优化代码的好习惯。使用此方法进行矩阵更新非常有优势。

      程序1:

      #include 
      int main(void)
      {
          int fact[5];
          fact[0] = 1;
        
          // Overhead of managing a counter
          // just for 4 iterations
          // is not a good idea
          for (int i = 1; i < 5; ++i) {
              fact[i] = fact[i - 1] * i;
          }
          return 0;
      }
      

      程式2:

      #include 
      int main(void)
      {
          int fact[5] = { 1, 1, 2, 6, 24 };
        
          // Here the same work is done
          // without counter overhead
          return 0;
      }
      
    • 避免循环计算:我们应避免任何值或多或少恒定的计算。内循环应尽可能少地进行计算。
      程序1:
      #include 
      int main(void)
      {
          int arr[1000];
          int a = 1, b = 5, c = 25, d = 7;
        
          // Calculating a constant expression
          // for each iteration is not good.
          for (int i = 0; i < 1000; ++i) {
              arr[i] = (((c % d) * a / b) % d) * i;
          }
          return 0;
      }
      

      程式2:

      #include 
        
      int main(void)
      {
          int arr[1000];
          int a = 1, b = 5, c = 25, d = 7;
        
          // pre calculating the constant expression
          int temp = (((c % d) * a / b) % d);
        
          for (int i = 0; i < 1000; ++i) {
              arr[i] = temp * i;
          }
          return 0;
      }
      
    • 避免在循环中使用指针取消引用:指针取消引用会在内存中造成很多麻烦。因此最好将其分配给某个临时变量,然后在循环中使用该临时变量。
      程序1:
      #include 
      int main(void)
      {
          int a = 0;
          int* iptr = &a;
        
          // Dereferencing pointer inside loop
          // is costly
          for (int i = 1; i < 11; ++i) {
              *iptr = *iptr + i;
          }
          printf("Value of a : %d", a);
          return 0;
      }
      
      输出:
      Value of a : 55
      

      程式2:

      #include 
      int main(void)
      {
          int a = 0;
          int* iptr = &a;
        
          // Dereferencing pointer outside loop
          // and saving its value in a temp variable
          int temp = *iptr;
        
          for (int i = 1; i < 11; ++i) {
        
              // performing calculations on temp variable
              temp = temp + i;
          }
        
          // Updating pointer using final value of temp
          *iptr = temp;
        
          printf("Value of a : %d", a);
          return 0;
      }
      
      输出:
      Value of a : 55
      
    • 将寄存器变量用作内部循环的计数器:与存储在内存中的变量相比,访问存储在寄存器中的变量的速度要快得多。
      程序:
      #include 
      int main(void)
      {
          register int i = 0;
          register int j = 0;
          int n = 5;
        
          // using register variables
          // as counters make the loop faster
          for (i = 0; i < n; ++i) {
              for (j = 0; j <= i; ++j) {
                  printf("* ");
              }
              printf("\n");
          }
          return 0;
      }
      
      输出:
      * 
      * * 
      * * * 
      * * * * 
      * * * * *
      
  2. 快速数学
    • 避免不必要的整数除法:除法运算很慢,因此我们应尽量减少除法运算。
      程序:
      #include 
      int main(void)
      {
          int a = 100, b = 2, c = 5;
          // int d=a/b/c;    two division operators
          int d = a / (b * c); // single division operator
          return 0;
      }
      
    • 乘以2的幂进行除法:将左移(<<)用于乘法,将右移(>>)用于除法。位运算将比乘法和除法运算快得多。对于简单运算,编译器可能会自动优化代码,但在表达式复杂的情况下,始终建议使用位运算。
      例子 :

      Multiply by 6 : a= a<<1 + a<<2; 
      Multiply by 7 : a= a<<3 - a;
      Divide by 8 : a= a>>3; // division by power of 2
      
    • 简化表达式:有时我们可以通过简化表达式来减少某些操作。
      例子 :
      a*b + a*b*c + a*b*c*d ---> (a*b)*(1 + c*(1 + d)) 
       L.H.S can be Simplified to R.H.S
       L.H.S  : 6 multiplications and 2 additions
       R.H.S  : 3 multiplications and 2 additions
      
  3. 使用switch语句进行优化
    编译器以不同的方式转换switch语句。如果案例标签是小的连续整数值,则它将创建一个跳转表。这非常快,并且也不取决于案例标签的数量。如果案例标签较长且不连续,则它将创建比较树,即if … else语句。因此,在这种情况下,我们应该将最频繁使用的标签放在最前面,而最不频繁使用的标签应该放在最后。

    有时我们会在所有情况下看到很多重复的代码,只有一两个语句

    例子 :

    switch(expression)
    {
    case a:
           ........
           ........
           break;
    case b:
           ........
           ........
           break;
    case c:
           common statements;
           different statements;
           common statements;
           break;
    case d:
           common statements;
           different statements;
           common statements;
           break;   '
    case e:
           common statements;
           different statements;
           common statements;
           break;
    case f:
           common statements;
           different statements;
           common statements;
           break;
    default:
           break;      
    }
    

    我们可以将所有情况汇总在一起,并且只能编写一次公共语句,而在相关情况下使用另一个开关可以编写不同的语句。在这里,我们将案例c,d,e,f放在一起并编写公共语句,然后我们可以使用另一个开关在案例c,d,e,f中编写不同的语句。然后,在此切换之后,我们可以再次编写公共语句。

    switch(expression)
    {
    case a:
           ........
           ........
           break;
    case b:
           ........
           ........
           break;
    case c: 
    case d: 
    case e:   
    case f:
           common statements;
           switch(expression);
           {
           case c:
                  different statements;
                  break;
           case d:
                  different statements;
                  break;
           case e:
                  different statements;
                  break;
           case f:
                  different statements;
                  break;
           }  /*End of switch*/
           common statements;
           break;     
           
    default:
           break;      
    }/*End of switch*/
    
  4. 首选int而不是char或short
    我们应该始终首选int而不是char,因为C用整数执行char的所有操作。在将char传递给函数或算术运算之类的所有操作中,第一个char将被转换为整数,并且在编译该操作后,将其再次转换为char 。对于单个字符,这可能不会影响效率,但是假设同一操作在一个循环中执行了100次,则可能会降低程序的效率。
  5. 优先于前增量/减量,而不是后增量/减量
    在递增之前,它首先递增值,然后将值复制到变量位置,但是在递增之后,首先将值复制到临时变量,将其递增,然后将值复制到变量位置。如果循环后增加1000次,则会降低效率。
  6. 表达顺序评价
    • A || B  
      

      在这里,首先将对A进行评估,如果它为true,则不需要评估表达式B。因此,我们应该更喜欢在A处拥有一个大多数情况下都评估为true的表达式。

    • A && B  
      

      在这里,首先将对A进行评估,如果它为假,则无需评估表达式B。因此,我们应该更喜欢在A处拥有一个大多数情况下都为假的表达式。

想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。