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

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

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

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

宏工作原理

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

#include <stdio.h>
" B' x. {% U) F$ _( j#define STR "hello world"
) d" d* b' M, y7 K& {5 x5 |- J/*这是一个hello word程序*/
8 }, G4 A! F1 `' S) sint main(void)9 B# c' L! d$ b5 ~, Y: v% _. Z
{6 V) O2 S- ?  O  o+ d" }
    printf("%s",STR);
5 Q3 P7 D3 G  H( V6 A    return 0;
$ j$ t* A- T8 t6 s9 F& Y1 H}$ K8 E3 P) _& h: o

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

//gcc -E 生成预处理文件
4 Y) o2 c% u3 t$ Y7 Q. i  Qgcc -E hello.c -o hello.i
* s* p% n. K1 O  T

来大致看看hello.i文件

# 1 "hello.c"  h) u; ]+ Y- a  f& L" f4 U
# 1 "<built-in>"9 I0 @: r; H. s* F# X
# 1 "<command-line>"
8 @3 N. S' W. x! @7 i9 r2 x  f# 1 "/usr/include/stdc-predef.h" 1 3 4
, x& f# M) y3 k  ^; h- u#删除很多行
1 Z: Z- @# @$ S.......
& O, U8 h# i- [extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__));
  k; v7 W. M1 m, W6 |7 l8 L6 \# 912 "/usr/include/stdio.h" 3 4
! y$ u# c' r& V2 Fextern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));% {3 I# X7 B) t4 w8 ?& y- g5 S# ~; @

! _" N$ `; J& G1 G$ ?5 Zextern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ;
  Y( ~& p' H1 q0 h' o5 X/ M. X$ _
0 X8 X/ D( y( X. y+ b3 Y& I3 j8 xextern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));. q6 h* P$ s) m) `. r
# 942 "/usr/include/stdio.h" 3 4' ^7 k9 r# E( M! |7 Y, y

( ?: k) q: l2 ^# D9 J5 E# 2 "hello.c" 2
4 t7 N( |0 D  ^  P, U# 4 "hello.c"
6 g2 J5 W+ S, ]" z7 \int main(void)1 k% W  q3 X9 j! V- W  _
{  C# |5 l# @/ V
    printf("%s","hello world");/ [8 c, {! T6 ?2 I
    return 0;
; a8 t' _, D# P}
8 s% l3 b! M; \+ }- T. `

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

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

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

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

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

    & l! G/ ]) x  O8 o& o
    • 不带参形式(有的地方也称对象形式object-like)。

      #define PI 3.1415926f
      3 h/ t9 y; ?- b& e; p
    • 带参形式(有的地方也称为函数形式function-like)。

      #define SQUARE(x) ((x)*(x))6 o$ K& Y7 r7 I7 k4 x4 e
      ( b/ V0 w3 X: ~3 z* ]. q+ ~( k# h
面试问题1

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

(A)  1

(B)  3

(C)  4

(D) 编译错误

#include <stdio.h> 8 P5 F4 `2 X) U
#define PRINT_HELLO(i, times) do \
  H& E8 [/ A* q7 _       { \
) ~+ e! }8 M8 }0 a7 r         if (i++ < times) \
4 C" R$ e. N- c) r3 p         { \
) c( n4 V& m" l# t1 K           printf("嵌入式客栈\n"); \   T9 B8 I% B7 p( o5 x
           continue; \ ) w/ `6 }% w3 W# E" m- ~' H
           } \
8 U9 \9 q; ?% P( c8 u0 {( j( m$ v0 y       }while(1) 9 \. i* o6 w6 {& s4 |) Z
  . \4 l( k6 N! P# W
int main() 8 j" B: l9 X7 d5 [# y# S
{
0 j) d0 A3 A9 M2 Z9 h/ ]    PRINT_HELLO(0, 3);
* s! Z( ]/ n* b, V! x; o    return 0; ; c1 k( M- ^9 s4 L8 L
}
! `% Q& }: N6 P

答案:D

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

面试问题2

下述代码的输出是什么?

(A) 3

(B) 5

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

(D) 编译错误

#include <stdio.h> 5 i0 n0 n4 _& N! M2 S
#if A == 3 . @% t, e1 I) Z
    #define B 3
2 I" c  x! D) I#else
( s  [1 D, Y7 r' N5 [    #define B 5 + z3 K3 `: f" S& L* @' J5 \2 U
#endif 3 ^% Y& e( u7 M* j, n7 K
  4 E" a  S7 f% m. m. A4 j
int main() ; c' B$ S5 h* W: b5 N
{ % d9 B# r7 r) T: ~- |  O4 _
    printf("%d", B); - M3 e+ y- \$ |" O
    return 0;
  l5 I  h1 G5 f& n( Q}
0 t, q/ N/ x% F, z# ]

答案:B

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

面试问题3

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

(A) 嵌入式

(B) 客栈

(C) 编译错误

(D) 运行错误

#include <stdio.h>
2 U+ B; b- T7 k- j9 g5 I7 Y0 N#define X 3
" Z; ^& g+ |) ~6 W5 M# \2 @#if !X ' M  }' L2 O- i& ?7 s6 @5 C
    printf("嵌入式");
; l. G+ ^$ g2 j+ J0 S( n#else
2 P' J* ?) X+ T    printf("客栈");   
# \  D# b9 [: Z) l# z#endif
; A* {" a; T+ @# pint main() 9 C; t! H2 _& Q9 `  ]
{
- d  W9 o1 w' C) C    return 0; ( {: L9 O- \% }1 Q0 s2 `$ E
}7 t2 @* K1 J: i) z( A' [

答案:C 编译错误

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

#这里还有stdio.h的包含内容
8 S" p$ C, m( q* F* k% Y8 qprintf("客栈");
& U7 T0 S. ]9 f  V) Gint main()0 ]7 K. R3 p' m  v9 @
{' T) F$ u& U+ \( R, m* N
    return 0;
: I& m! N: S" b. x( v  M# X}# U& f0 H7 \3 y

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

面试问题4

下述代码的输出应该是?

(A) 嵌入式

(B) 客栈

(C) 嵌入式 或 客栈

(D) 编译错误

#include <stdio.h>
  T& x! A" ?7 f. x) q#define IS_EQUAL(X, Y) X == Y
1 p' G+ N0 y* H. W. C& mint main()
$ W  V* B3 h  {9 P{
0 @0 r) h; Y; Y" t0 \( \" b    #if IS_EQUAL(X, 0)
9 M7 N; x4 ?1 s7 H6 t        printf("嵌入式");
7 b- q; N  P# S% f- A  B( s" _; K    #else
) P& Q3 w  s" _# D% A        printf("客栈");
! K+ L9 p8 D; e5 m    #endif
# \, p  {9 B! ?2 |0 n    return 0;
1 w4 _4 R% Y8 `, Y( q2 i}
5 k! B( c6 }+ Y& L. {

答案:A

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

面试问题5

下述代码的输出应该是?

(A) 20

(B) 2000

(C) 0

(D) 编译错误

#include <stdio.h> ( o& a' N% `8 I( p
#define SQUARE(x) x*x
7 S/ E9 c: |" q( W2 V. p7 ~' hint main()   C8 g4 ~5 t. s+ q  L8 Q- t. v
{ & o9 V- A* @$ O
  int x;
! F) o- B5 x8 g  q( j  x = 2000/SQUARE(10);
1 j2 k1 O1 Y. X; q5 K  ?  printf("%d", x); 3 O5 G( }" c, X0 Y* s! }8 G4 h. s0 K5 z
  return 0;
$ x; y/ q2 Z: _& H  q4 o} " F  b! ?8 g; b/ e$ a5 N

答案:B

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

#define SQUARE(x) ((x)*(x))
0 a4 w) K; t! ?6 a0 r. q
面试问题6

下述代码的输出应该是?

(A) 编译错误

(B) %s Embedded Inn

(C) Embedded Inn

(D) %s Embedded Inn Embedded Inn

# include <stdio.h> . q: G* J3 K, y' G5 V
# define scanf  "%s Embedded Inn " : F2 J/ w2 L3 g6 ^0 B! S
int main()
9 _% c6 N  q; @( D3 W{
9 K5 C1 b7 H( \8 J* ]! w4 ?% @$ }+ p; ~   printf(scanf, scanf); 3 m3 F: F; a( u& H3 c& G
   return 0; 7 j5 E% b( W9 O$ e; q
} - x6 |% }: F" `8 `& ]* J7 O1 R

答案:D

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

printf(“%s Embedded Inn”,“%s Embedded Inn”);
# m9 C" Z* P5 d4 l' \6 i
面试问题7

下述代码的输出应该是?

(A) 编译错误

(B) 嵌入式客栈

(C) 客栈客栈

(D) 嵌入式嵌入式

#include <stdio.h> 4 G  E+ u$ V# O9 @9 J
#define STR "嵌入式" ' i7 g4 H0 f$ O( [( x# H
int main() 8 c8 P/ Q2 s' c
{ 5 u2 Z0 z6 v, B) R
  printf("%s",STR);
8 e: j2 J3 g1 K$ u7 j; J  
0 [3 v, G  U1 P/ e$ d% X  #define STR "客栈"
" |3 I) L1 I$ v* Z7 |  " T  a8 g; @. t" D) G+ Q/ |
  printf("%s",STR);0 n5 j* v2 E4 B/ d
  return 0;
: x  T$ I; r) F5 e}
/ d9 m) Q; k; ?) f4 C: G% u5 Y1 A" T

答案:B

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

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

下述代码的输出应该是?

(A) 100

(B) 编译错误

(C) 0

(D) 1

#include<stdio.h>  
& L, K! q9 s# g* q7 F$ b, D2 f, K#define ADHESION(x,y) x##y  
) o& n- ]; R3 M" `& ?3 K- Zint main()  
; H& v# c5 s, D/ D1 f# D{  ; _+ c( O3 o, j
   int var1 = 100;  ' ^" b& F3 y' W' q
   printf("%d", ADHESION(var,1));  / I/ z  g7 A, n0 j7 e$ f! f; V$ _
   return 0;  
* }" V* C7 j  ^4 Q( G/ |7 o7 z}
, ^7 y" s* y4 a0 o, L- p1 O& q

答案:A

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

printf("%d", var1);
9 Z1 @4 @* Q. @, E# C9 n9 V$ O- f
面试问题9

下述代码的输出应该是?

(A) 6666.6

(B) 666.6

(C) 编译错误

(D) 无效值

#include <stdio.h> " Y5 ^% _# f! D3 ?+ i
#define MAX 6666.6f
9 _+ ^& N" A" p3 D* B* \int main() " J9 Q' I$ V1 K( X' h
{
2 n* w/ j( W. {3 ^   float MAX = 666.6; 9 ^6 }/ m. V( ]- x' S
   printf("%f ", MAX); 9 F1 ~* u4 C" b$ ]( ?" Q
   return 0;
8 a" g8 a3 u) E! }9 m} 8 Z# {" i0 G' V/ s8 e

答案:C

解析:展开一看便知。

int main()! k" b- o% |2 O2 s
{/ M" |6 Z( }6 u5 E' u
   float 6666.6 = 666.6;  //常数不可为左值: s6 v9 _$ O+ p; U# r5 G4 f, J' u
   printf("%d ", 6666.6);
1 C6 i2 u/ ~4 k  f+ i) Y   return 0;
# I1 I' Q, d( x' s6 ~; ^} $ X( S" _) X+ e$ |& B. |
面试问题10

下述代码的输出应该是?

(A) 编译错误

(B) 嵌入式客栈

(C) MAIN

(D) main

#include <stdio.h>
5 V9 E4 a+ M+ G3 o9 V( n" o& d#define macro(n, a, i, m) m##a##i##n
3 j4 a$ @" x! ?9 N/ E* A#define MAIN macro(n, a, i, m) ; t( V" f1 t/ X+ v. U0 F+ c
  3 [0 ~0 ^, ~) |9 E8 x5 u
int MAIN()
) M! R9 y1 K( L5 q# ~{ ! P: K- t0 B% n8 ^1 k
    printf("嵌入式客栈");
2 P; r' \& c1 x! Q5 Y; a    return 0; , y* q- h. S- @5 H' X/ a
}
2 B$ p* y6 K: d, P, P/ y  w

答案:B

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

总结一下

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

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


8 q6 ]8 n1 `; F0 L- l, L" z) B
收藏 评论0 发布时间:2020-8-29 23:28

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版