你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

分享总结一些关于宏的常见面试问题

[复制链接]
gaosmile 发布时间:2020-8-29 23:28

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

宏工作原理

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

#include <stdio.h>
: y+ L7 W# Z' ^/ U#define STR "hello world"; T3 M5 @3 D  [2 ^
/*这是一个hello word程序*/" H/ @- p4 ~# S7 v" h9 g% b2 P
int main(void)
) @; Q  N3 D! Z( r5 `! k{( [/ `0 q- j4 m, X
    printf("%s",STR);
: U& B1 X( l2 e/ b9 z    return 0;! z% v& k( e( t2 r3 k
}7 {* z; |2 K4 j

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

//gcc -E 生成预处理文件9 Z4 z$ n- x$ Z: |. E
gcc -E hello.c -o hello.i
% M( D+ ?7 t6 f+ ~9 i

来大致看看hello.i文件

# 1 "hello.c"
  R/ x/ [! U+ U8 m5 O7 ?+ N# 1 "<built-in>"
; d  Z- o: S7 F( `# 1 "<command-line>"
; r+ O; [1 F3 C: ?# 1 "/usr/include/stdc-predef.h" 1 3 4
9 V- w: ?( R& Y1 Y1 z/ ]#删除很多行
$ \) A! v. D3 @2 S.......
6 R4 `3 v; v' J! g3 n# L$ oextern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
2 u2 Z  c. E0 G5 d4 G# 912 "/usr/include/stdio.h" 3 4
5 A) E* I4 P  ]3 r- ]+ Hextern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));% U3 a1 n' u% e7 M$ {+ g0 a% L
* h8 W4 _- Y* G( V. B) t! m
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
' K9 J9 C6 [: j- l
2 d% J* z+ a. j) e- X2 ]  ]extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
9 T; @. d+ l$ b2 q+ H- g7 e6 j& q. m# 942 "/usr/include/stdio.h" 3 46 }5 q3 D( @0 m; S; I0 @' C

: t. I  ~% I/ R# 2 "hello.c" 2  @$ Q/ W/ k! ~
# 4 "hello.c"2 O6 @- ]0 s5 g9 ]
int main(void)
& U# S" F) i; R# R/ ^, T2 i7 `{
: P6 Y7 r, C( A, i+ P    printf("%s","hello world");* H3 d& w$ ^2 V6 e' E% ^
    return 0;: _$ Z: D2 D+ h2 j( [5 y# Y
}
, G6 t( b& b0 ]1 P' _5 y2 R/ n- K

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

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

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

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

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


    8 M6 ~* Q( t, m. n
    • 不带参形式(有的地方也称对象形式object-like)。

      #define PI 3.1415926f
      ! z$ Z/ k! y, ]3 I0 H- T5 }
    • 带参形式(有的地方也称为函数形式function-like)。

      #define SQUARE(x) ((x)*(x))
      ! O+ {6 ]; z8 m; l& |7 v0 S

      9 U. J/ F, |) ^8 i8 |8 ~/ T
面试问题1

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

(A)  1

(B)  3

(C)  4

(D) 编译错误

#include <stdio.h>
% t5 I1 D" p/ E: m( ~8 }( F#define PRINT_HELLO(i, times) do \ 8 a6 n4 e, [, s! ]
       { \
( |$ E0 t! u9 ~; e+ M         if (i++ < times) \ # d/ O8 j, [6 w9 B5 m5 {. ?3 }& ?
         { \
8 U6 [( P' B3 r0 N7 n3 Y           printf("嵌入式客栈\n"); \ # F8 l5 i* Z; N3 ~7 l
           continue; \ * y8 j" W: }$ i
           } \
- |- K9 ?, M: `7 e  ~       }while(1) ; R* }' ?) F! W7 S( E8 L$ y
  
' d0 L1 z2 P0 }int main()
6 R7 |) V- f  Y! I{ . C3 \0 p- A5 r
    PRINT_HELLO(0, 3); ; l3 p. {/ ]0 K' j& m
    return 0; 0 g) s1 F9 a! w$ m0 u9 R
} 9 i; W: U. [9 k, ?0 {; I2 z

答案:D

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

面试问题2

下述代码的输出是什么?

(A) 3

(B) 5

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

(D) 编译错误

#include <stdio.h> & T# i4 `6 k6 s1 g0 V
#if A == 3 1 \  i; c) O% e7 u6 V8 o
    #define B 3 3 M6 `: X: [0 v. I
#else
" K6 p3 u6 ?( e6 F, v6 r    #define B 5 % E0 Z) f" r3 s/ k4 T0 {: r& `
#endif
, C7 X1 w- Z; X5 i9 }; h  / ~  Y. \6 |8 U& `
int main() 7 ?3 [$ [& r! O" e
{
! g, W6 J% D1 Q; p$ J    printf("%d", B); 1 M5 a. H6 R/ f" V  k" R
    return 0;
# }0 S& A* F' \$ E( N, @! q}
1 U2 `7 p6 g& P: @- v2 q5 Q

答案:B

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

面试问题3

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

(A) 嵌入式

(B) 客栈

(C) 编译错误

(D) 运行错误

#include <stdio.h> # e: T' Z) L+ I  h0 W0 L' T9 i+ U, p& T
#define X 3 ( X$ X, O* S- x+ H) h: h6 U
#if !X 9 q6 F: T/ c4 ~  g2 E
    printf("嵌入式"); 5 a$ g; Y/ K5 D- `% }
#else
# n6 P. G9 Q* b) F1 Z1 m    printf("客栈");   
  i7 z8 O7 q: v' |+ \#endif
2 }1 P& N3 @# p: u/ W6 b/ ~/ ]int main() % i2 b# |6 v0 c+ ~
{
# H% l6 ]4 c; q" D6 i    return 0;
9 P# L2 H- ]0 U1 o}; F: \7 [) C, h" G

答案:C 编译错误

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

#这里还有stdio.h的包含内容5 W( c# @: d4 X; S
printf("客栈");
2 r/ J: t& t" R5 [5 uint main()
7 [; w0 W( k, X, w$ E( V{3 \: `8 D" l9 Q0 ~
    return 0;
. e8 s! Z$ D5 }* S}
) ~  C; s& F% _& J

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

面试问题4

下述代码的输出应该是?

(A) 嵌入式

(B) 客栈

(C) 嵌入式 或 客栈

(D) 编译错误

#include <stdio.h> - Y/ m3 G! s1 A# Y8 y
#define IS_EQUAL(X, Y) X == Y
$ J( n. J3 d7 b: lint main() % T7 Y: }6 [1 B* T
{
/ `+ n* d$ B# w2 X: X. P    #if IS_EQUAL(X, 0) $ b6 c% O7 y/ E# B% n
        printf("嵌入式"); 4 D& E" n1 m; j; L( W' E* Y1 B/ i( A
    #else
2 w# s9 l' |. K  q        printf("客栈"); # q  b8 N& [+ p% a" Z: X
    #endif " `2 n: E. ]6 X
    return 0;
4 w$ J+ O9 E% m1 c6 m. ~: x} : ]. [& I2 d3 f( y

答案:A

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

面试问题5

下述代码的输出应该是?

(A) 20

(B) 2000

(C) 0

(D) 编译错误

#include <stdio.h> 9 q/ F) m5 A3 ~" Y- N& U3 G
#define SQUARE(x) x*x 0 X- \: @9 {  }3 ?' w$ I
int main() 9 ^8 \* r7 X: b; b9 _2 i% t
{ 0 }7 J/ s5 ~' _' \% t0 B/ ^( r
  int x; - O7 b4 _. I# J2 Q) {+ d
  x = 2000/SQUARE(10);
; w7 a3 n/ [$ B, j& ?  printf("%d", x); ( ~8 \: h% j% W
  return 0; - w# |& |9 o1 O" v9 @3 M
} 5 o6 P' R  @/ S( v7 C. ^

答案:B

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

#define SQUARE(x) ((x)*(x))- q$ _5 h; T5 f
面试问题6

下述代码的输出应该是?

(A) 编译错误

(B) %s Embedded Inn

(C) Embedded Inn

(D) %s Embedded Inn Embedded Inn

# include <stdio.h>
3 v8 b8 k6 N  M4 k- e' }* X/ d# define scanf  "%s Embedded Inn "
; F* ~+ i, n; T3 kint main() % S: Q+ s% Y2 Q/ u' U# l. z: e. t
{ 8 L& `( H5 g& E- ~0 q
   printf(scanf, scanf); 0 i! {2 x# l9 f8 ?0 w
   return 0;
( X8 P: t( L2 f4 H* z. f! k, `4 U} ! E; i0 _' t2 L( x( ]* D; m3 l

答案:D

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

printf(“%s Embedded Inn”,“%s Embedded Inn”);
3 o) J3 y  M# e. e; y! K: q" t  R
面试问题7

下述代码的输出应该是?

(A) 编译错误

(B) 嵌入式客栈

(C) 客栈客栈

(D) 嵌入式嵌入式

#include <stdio.h>
$ O- }1 |5 e! l& T$ h+ l4 P#define STR "嵌入式"
/ {7 k& s. T9 B3 \# z, T, r9 |int main()
; j6 j/ Q$ l+ R% j9 [: \' x{ / P" Z1 a: z& d9 Z( ^, R& `0 @. ?
  printf("%s",STR);
8 P! m; L' m( ?8 E# C  
6 L  j) Y+ U$ L2 [2 l) |  #define STR "客栈"
' S: ~& L! v3 D( q% [) D; p* A6 Y. d  ) c3 y" b5 n7 z2 D6 ^% K9 f
  printf("%s",STR);
/ Q8 I- y& R8 D6 W, {  return 0; . @2 {5 Y0 x& ?. g. _, a* b" W. a1 F
}
4 f0 X. x3 U# T4 h+ X# b

答案:B

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

微信图片_20200829232820.png
面试问题8

下述代码的输出应该是?

(A) 100

(B) 编译错误

(C) 0

(D) 1

#include<stdio.h>  
% F2 q4 v- i- f#define ADHESION(x,y) x##y  0 J  i' [! \* J9 y9 s
int main()  * x4 w& r6 F, e' h& {5 C$ d
{  ! F3 b& m  U' j% [
   int var1 = 100;  
# i5 X" ^! n) m0 b, j   printf("%d", ADHESION(var,1));  
4 f, g7 B5 Z" x( y$ s   return 0;  " L  m% R! D! d4 h
} " c0 w! n% s# w8 }5 ]2 J

答案:A

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

printf("%d", var1);
3 ?9 x; `/ x, b, ]. v8 B
面试问题9

下述代码的输出应该是?

(A) 6666.6

(B) 666.6

(C) 编译错误

(D) 无效值

#include <stdio.h>
, w" @+ t# n6 G! c& s% _' d#define MAX 6666.6f  O! ~5 e3 G0 v6 G
int main() ! Z6 N) Z0 P2 D) u6 s# L& p" r3 p
{ 1 B6 F% \" |6 Q  h, e# n
   float MAX = 666.6; % l: f2 K4 b2 P2 y" G
   printf("%f ", MAX); / B  \$ S9 N6 F) u, p2 l2 q
   return 0;
0 z: \4 o( s- b0 h0 F+ Y- t} . Y! W, J! u( O1 |* q7 |$ Q* v

答案:C

解析:展开一看便知。

int main()
. @! J, d; i2 `{
! p' j/ S" u6 M6 Y   float 6666.6 = 666.6;  //常数不可为左值
  m' B5 [3 U( Z$ e6 C   printf("%d ", 6666.6);+ j* L5 t+ ~% C4 d
   return 0;
7 r5 h( g, k8 ?2 v, D8 b! ~}
. T$ b7 z) r! Q* B- x
面试问题10

下述代码的输出应该是?

(A) 编译错误

(B) 嵌入式客栈

(C) MAIN

(D) main

#include <stdio.h> ; M% H6 T+ d) R+ w, D* ]/ M
#define macro(n, a, i, m) m##a##i##n # N5 }' n6 Y- A) E4 o, D
#define MAIN macro(n, a, i, m) ; J& R- N# b. S/ I! a
  - p! L' z2 [5 r+ R- C% m9 ?
int MAIN()
( b2 c0 y' V, k( n6 H1 [! `{ , M' L; f' B4 J* G6 A' P6 G: k
    printf("嵌入式客栈"); 4 f$ d, W9 O- l# J* {# c9 C
    return 0; 2 n7 N2 U7 f5 v# N0 p6 v
}- Q" j8 C! Q7 l" A; h) K, {

答案:B

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

总结一下

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

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


7 O# w0 J1 y/ t5 W4 h3 {) p
赞 收藏 评论0 发布时间:2020-8-29 23:28

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版