zoukankan      html  css  js  c++  java
  • [收藏] 宏工作原理以及典型面试10问

    关注、星标嵌入式客栈,精彩及时送达

    [导读] C语言中宏是非常有价值的语言特性之一,也是面试中必考察的要点之一,本文来分享总结一些关于宏的常见面试问题。希望能帮助到有需要的小伙伴们。

    宏工作原理

    以hello word程序为例来看看,将下述代码存成hello.c

    #include <stdio.h>
    #define STR "hello world"
    /*这是一个hello word程序*/
    int main(void)
    {
        printf("%s",STR);
        return 0;
    }
    

    为了说明问题,这里用下面的命令进行显式预处理,将得到hello.i文件,实际编译过程中,会自动完成。

    //gcc -E 生成预处理文件
    gcc -E hello.c -o hello.i
    

    来大致看看hello.i文件

    # 1 "hello.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    #删除很多行
    .......
    extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
    # 912 "/usr/include/stdio.h" 3 4
    extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
    
    extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
    
    extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
    # 942 "/usr/include/stdio.h" 3 4
    
    # 2 "hello.c" 2
    # 4 "hello.c"
    int main(void)
    {
        printf("%s","hello world");
        return 0;
    }
    

    上面这步操作做了三件事情:

    • 删除注释:删除所有注释。注释仅供程序员理解代码,注释对机器没有用。因此预处理器在预处理过程中会删除注释,因为注释在执行过程中是不需要的,也不会被执行。所以注释尽管写不影响程序的逻辑,当然写的过也未必是好事,过少也不是好事。个人理解一份好的代码应尽量少注释,应该通过合理的命名习惯,良好的编程风格来提高可读性,在一些关键复杂算法处则应清晰的加上注释。在hello.c中的注释  /*这是一个hello word程序*/  在预处理后被删除掉了。

    • 文件包含:包含程序需要的所有文件。C语言中使用#include,这是预处理器的指令,告诉预处理器包含指定文件的内容。例如#include将告诉预处理器将stdio.h中所有的内容包含进来。也可以使用双引号-#include “stdio.h” 注意:如果使用尖括号,则在编译器包含路径中搜索文件。如果文件名用双引号包起来,则搜索路径将扩展为除了编译器包含路径外的当前目录下。

    • 宏展开替换:比如上例中宏STR在预处理时就被展开替换了。宏有两种常见形式:

      大致说明了宏的工作原理,来看看一些常见的面试问题:

      • 不带参形式(有的地方也称对象形式object-like)。

        #define PI 3.1415926f
        
      • 带参形式(有的地方也称为函数形式function-like)。

        #define SQUARE(x) ((x)*(x))
        

    面试问题1

    如下代码,问有多少个"嵌入式客栈"会被打印?

    (A)  1 

    (B)  3 

    (C)  4 

    (D) 编译错误

    #include <stdio.h> 
    #define PRINT_HELLO(i, times) do  
           {  
             if (i++ < times)  
             {  
               printf("嵌入式客栈
    ");  
               continue;  
               }  
           }while(1) 
      
    int main() 
    { 
        PRINT_HELLO(0, 3); 
        return 0; 
    } 
    

    答案:D

    解析:PRINT_HELLO宏在预处理器时被扩展。宏展开后,if表达式变为:if(0 ++ <3)。0是一个常数,常数如何自增呢?,因此应用增量运算符会产生编译时错误。

    面试问题2

    下述代码的输出是什么?

    (A) 3

    (B) 5 

    (C) 3 或者 5 取决于X的值 

    (D) 编译错误

    #include <stdio.h> 
    #if A == 3 
        #define B 3 
    #else 
        #define B 5 
    #endif 
      
    int main() 
    { 
        printf("%d", B); 
        return 0; 
    } 
    

    答案:B

    解析:乍一看,输出似乎是编译时错误,因为尚未定义宏A,所以A是不等于3的,所以会将B定义为5。你如不信,也可以用上面的办法gcc -E hello.c -o hello.i来验证,或者编译运行一遍。

    面试问题3

    问:针对下述代码,哪个答案正确?

    (A) 嵌入式

    (B) 客栈 

    (C) 编译错误

    (D) 运行错误

    #include <stdio.h> 
    #define X 3 
    #if !X 
        printf("嵌入式"); 
    #else 
        printf("客栈");    
    #endif 
    int main() 
    { 
        return 0; 
    }
    

    答案:C 编译错误

    解析:程序编译三部曲:预处理、汇编、链接,那么在预处理时,上述代码就变成下面这样:

    #这里还有stdio.h的包含内容
    printf("客栈"); 
    int main()
    {
        return 0;
    }
    

    printf在main外面被调用了,所以编译会出错。

    面试问题4

    下述代码的输出应该是?

    (A) 嵌入式 

    (B) 客栈

    (C) 嵌入式 或 客栈

    (D) 编译错误

    #include <stdio.h> 
    #define IS_EQUAL(X, Y) X == Y 
    int main() 
    { 
        #if IS_EQUAL(X, 0) 
            printf("嵌入式"); 
        #else 
            printf("客栈"); 
        #endif 
        return 0; 
    } 
    

    答案:A

    解析:条件宏#if IS_EQUAL(X,0)扩展为#if X ==0。预处理结束后,所有未定义的宏均使用默认值0初始化。

    面试问题5

    下述代码的输出应该是?

    (A) 20 

    (B) 2000 

    (C) 0

    (D) 编译错误

    #include <stdio.h> 
    #define SQUARE(x) x*x 
    int main() 
    { 
      int x; 
      x = 2000/SQUARE(10); 
      printf("%d", x); 
      return 0; 
    } 
    

    答案:B

    解析:预处理器用10*10替换SQUARE(10),表达式变为 x = 2000/10 * 10,x的值计算为2000。如前所说,应定义如下:

    #define SQUARE(x) ((x)*(x))
    

    面试问题6

    下述代码的输出应该是?

    (A) 编译错误

    (B) %s Embedded Inn 

    (C) Embedded Inn 

    (D) %s Embedded Inn Embedded Inn

    # include <stdio.h> 
    # define scanf  "%s Embedded Inn " 
    int main() 
    { 
       printf(scanf, scanf); 
       return 0; 
    } 
    

    答案:D

    解析:在编译的预处理阶段之后,printf语句将变为。

    printf(“%s Embedded Inn”,“%s Embedded Inn”);
    

    面试问题7

    下述代码的输出应该是?

    (A) 编译错误

    (B) 嵌入式客栈 

    (C) 客栈客栈

    (D) 嵌入式嵌入式

    #include <stdio.h> 
    #define STR "嵌入式" 
    int main() 
    { 
      printf("%s",STR); 
      
      #define STR "客栈" 
      
      printf("%s",STR);
      return 0; 
    }
    

    答案:B

    解析:如果重新定义预处理程序指令,则预处理器不会给出任何错误,它可能会发出警告。预处理器在使用之前获取新值,并将其替换。

    面试问题8

    下述代码的输出应该是?

    (A) 100 

    (B) 编译错误 

    (C) 0 

    (D) 1

    #include<stdio.h>  
    #define ADHESION(x,y) x##y  
    int main()  
    {  
       int var1 = 100;  
       printf("%d", ADHESION(var,1));  
       return 0;  
    } 
    

    答案:A

    解析:运算符##称为“令牌粘贴(Token-Pasting)”或“合并(Merge)”运算符。它将两个符合合并为一个。因此在预处理之后,printf变为。

    printf("%d", var1); 
    

    面试问题9

    下述代码的输出应该是?

    (A) 6666.6

    (B) 666.6 

    (C) 编译错误

    (D) 无效值

    #include <stdio.h> 
    #define MAX 6666.6f
    int main() 
    { 
       float MAX = 666.6; 
       printf("%f ", MAX); 
       return 0; 
    } 
    

    答案:C

    解析:展开一看便知。

    int main()
    {
       float 6666.6 = 666.6;  //常数不可为左值
       printf("%d ", 6666.6);
       return 0;
    } 
    

    面试问题10

    下述代码的输出应该是?

    (A) 编译错误 

    (B) 嵌入式客栈

    (C) MAIN

    (D) main

    #include <stdio.h> 
    #define macro(n, a, i, m) m##a##i##n 
    #define MAIN macro(n, a, i, m) 
      
    int MAIN() 
    { 
        printf("嵌入式客栈"); 
        return 0; 
    }
    

    答案:B

    解析:不注意可能会选A,认为将MAIN敲成大写了,其实仔细一看,通过前面两个宏以及粘连操作符##将MAIN替换成main,所以没有问题,这个题目比较骚,主要考察细心以及粘连操作符。

    总结一下

    面试小提示:实际笔试中,只有掌握了宏的基本操作原理,以及宏预处理的本质,在解题时细心展开,一般而言不会有什么问题。

    本文总结了宏的基本工作原理,以及10个比较典型的面试问题,相信对于宏理解不深的盆友会有些许帮助。

    本文辛苦原创,如喜欢请点赞/在看/分享支持,不胜感激!

    END

    往期精彩推荐,点击即可阅读

    ▲Linux内核中I2C总线及设备长啥样? 

    ▲学Linux驱动:应先了解总线驱动模型

    ▲看思维导图:一文带你学Verilog HDL语言

  • 相关阅读:
    Codeforces Beta Round #92 (Div. 2 Only) B. Permutations 模拟
    POJ 3281 Dining 最大流 Dinic算法
    POJ 2441 Arrange the BUlls 状压DP
    URAL 1152 Faise Mirrors 状压DP 简单题
    URAL 1039 Anniversary Party 树形DP 水题
    URAL 1018 Binary Apple Tree 树形DP 好题 经典
    pytorch中的forward前向传播机制
    .data()与.detach()的区别
    Argparse模块
    pytorch代码调试工具
  • 原文地址:https://www.cnblogs.com/embInn/p/14038108.html
Copyright © 2011-2022 走看看