34 函数与宏分析

1 函数与宏

  • 宏 or 函数?

    #define RESET(p,len)	
        while(len > 0)		
            ((char*)p)[--len] == 0
    
    void reset(void* p,int len)
    {
        while(len > 0)
        {
            ((char*)p)[--len] = 0;
        }
    }
    
  • 宏与函数的区别

    • 宏是由预处理器直接替换展开的,编译器不知道宏的存在
    • 函数是由编译器直接编译的实体,调用行为由编译器决定
    • 多次使用宏会导致最终可执行程序的体积增大
    • 函数是跳转执行的,内存中只有一份函数体存在
    • 宏的效率比函数要高,因为是直接展开,无调用开销
    • 函数调用时会创建活动记录,效率不如宏
  • 示例:函数与宏

    • Demo:将数组中的每个字节重置为 0
      #include <stdio.h>
      
      #define RESET(p, len)          
          while( len > 0 )           
              ((char*)p)[--len] = 0
      
      void reset(void* p, int len)
      {
          while( len > 0 ) 
              ((char*)p)[--len] = 0;
      }
      
      int main()
      {
          int array1[] = {1, 2, 3, 4, 5};
          int array2[] = {1, 2, 3, 4, 5};
          int len1 = sizeof(array1);  // array1数组的字节数:20,≠5
          int len2 = sizeof(array2);  // array2数组的字节数:20,≠5
          int i = 0;
          
          RESET(array1,len1);
          for(i = 0; i < 5; i++)
          {
              printf("array1[%d] = %d
    ", //i, array1[i]);
          }
          
          reset(array2,len2);
          for(i = 0; i < 5; i++)
          {
              printf("array2[%d] = %d
    ", i, array2[i]);
          }
          
          return 0;
      }
    
    • 编译运行

      array1[0] = 0
      array1[1] = 0
      array1[2] = 0
      array1[3] = 0
      array1[4] = 0
      array2[0] = 0
      array2[1] = 0
      array2[2] = 0
      array2[3] = 0
      array2[4] = 0
      
  • 宏与函数的选择

    • 可以用函数完成的功能绝对不用宏

    • 宏的定义中不能出现递归定义

    • 宏的效率比函数稍高,但是其副作用巨大

    • 宏是文本替换,参数无法进行类型检查

      • Demo1:错误使用宏时

        #include <stdio.h>
        
        #define RESET(p, len)          
            while( len > 0 )           
                ((char*)p)[--len] = 0
        
        void reset(void* p, int len)
        {
            while( len > 0 ) 
                ((char*)p)[--len] = 0;
        }
        
        int main()
        {
            int array[] = {1, 2, 3, 4, 5};
            int len = sizeof(array);
            int i = 0;
            
            RESET(6,len);
            for(i = 0; i < 5; i++)
            {
                printf("array[%d] = %d
        ", //i, array[i]);
            }
            
            return 0;
        }
        
        • 编译不报错,运行错误
        段错误
        
        • Demo2:错误调用函数时
        #include <stdio.h>
        
        #define RESET(p, len)          
            while( len > 0 )           
                ((char*)p)[--len] = 0
        
        void reset(void* p, int len)
        {
            while( len > 0 ) 
                ((char*)p)[--len] = 0;
        }
        
        int main()
        {
            int array[] = {1, 2, 3, 4, 5};
            int len = sizeof(array);
            int i = 0;
            
            reset(6,len);
            for(i = 0; i < 5; i++)
            {
                printf("array[%d] = %d
        ", i, array[i]);
            }
            
            return 0;
        }
        
        • 编译时直接报错
        test.c: In function ‘main’:
        test.c:19: warning: passing argument 1 of ‘reset’ makes pointer from integer without a cast
        test.c:7: note: expected ‘void *’ but argument is of type ‘int’
        
  • 示例:宏的副作用

    • Demo

      #include <stdio.h>
      
      #define _ADD_(a, b) a + b
      #define _MUL_(a, b) a * b
      #define _MIN_(a, b) ((a) < (b) ? (a) : (b))
      
      int main()
      {
          int i = 1;
          int j = 10;
          
          printf("%d
      ", _MUL_(_ADD_(1, 2), _ADD_(3, 4)));  //预期:(1 + 2) * ( 3 * 4) => 结果:11
          printf("%d
      ", _MIN_(i++, j));  //预期:1 < 10 ? 1 : 10 => 结果:2
          
          return 0;
      }
      

3 宏的妙用

  • 用于生成一些常规性的代码

  • 封装函数,加上类型信息

  • 示例:宏的妙用

    • Demo

      #include <stdio.h>
      #include <malloc.h>
      
      //函数封装:加上类型信息
      #define MALLOC(type, x)   (type*)malloc(sizeof(type)*x)
      //函数封装:释放指针指向的动态内存,并立即将产生的野指针赋值为空
      #define FREE(p)           (free(p), p=NULL)
      
      //封装打印函数
      #define LOG_INT(i)        printf("%s = %d
      ", #i, i)
      #define LOG_CHAR(c)       printf("%s = %c
      ", #c, c)
      #define LOG_FLOAT(f)      printf("%s = %f
      ", #f, f)
      #define LOG_POINTER(p)    printf("%s = %p
      ", #p, p)
      #define LOG_STRING(s)     printf("%s = %s
      ", #s, s)
      
      //封装遍历函数,while是为了保证使用的是代码块,防止变量的重复命名
      #define FOREACH(i, n)     while(1) { int i = 0, l = n; for(i=0; i < l; i++)
      #define BEGIN             {
      #define END               } break; } 
      
      int main()
      {
          int* pi = MALLOC(int, 5);
          char* str = "ABCD";
          
          LOG_STRING(str);
          
          LOG_POINTER(pi);
          
          FOREACH(k, 5)
          BEGIN
              pi[k] = k + 1;
          END
          
          //这里也可以使用FOREACH(k,5),不会发生变量名冲突
          FOREACH(n, 5)
          BEGIN
              int value = pi[n];
              LOG_INT(value);
          END
          
          FREE(pi);
          
          LOG_POINTER(pi);
          
          return 0;
      }
      
    • 编译运行

      str = ABCD
      pi = 0x8b4b008
      pi = (nil)
      value = 1
      value = 2
      value = 3
      value = 4
      value = 5
      pi = (nil)
      
原文地址:https://www.cnblogs.com/bky-hbq/p/13774048.html