|
[导读] C语言中宏是非常有价值的语言特性之一,也是面试中必考察的要点之一,本文来分享总结一些关于宏的常见面试问题。希望能帮助到有需要的小伙伴们。 宏工作原理以hello word程序为例来看看,将下述代码存成hello.c #include <stdio.h>#define STR "hello world"; T3 M5 @3 D [2 ^ /*这是一个hello word程序*/" H/ @- p4 ~# S7 v" h9 g% b2 P int main(void) {( [/ `0 q- j4 m, X printf("%s",STR); return 0;! z% v& k( e( t2 r3 k }7 {* z; |2 K4 j 为了说明问题,这里用下面的命令进行显式预处理,将得到hello.i文件,实际编译过程中,会自动完成。 //gcc -E 生成预处理文件9 Z4 z$ n- x$ Z: |. Egcc -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__));% 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__)) ; extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); # 942 "/usr/include/stdio.h" 3 46 }5 q3 D( @0 m; S; I0 @' C # 2 "hello.c" 2 @$ Q/ W/ k! ~ # 4 "hello.c"2 O6 @- ]0 s5 g9 ] int main(void) { printf("%s","hello world");* H3 d& w$ ^2 V6 e' E% ^ return 0;: _$ Z: D2 D+ h2 j( [5 y# Y } 上面这步操作做了三件事情:
#include <stdio.h> #define PRINT_HELLO(i, times) do \ 8 a6 n4 e, [, s! ] { \ if (i++ < times) \ # d/ O8 j, [6 w9 B5 m5 {. ?3 }& ? { \ printf("嵌入式客栈\n"); \ # F8 l5 i* Z; N3 ~7 l continue; \ * y8 j" W: }$ i } \ }while(1) ; R* }' ?) F! W7 S( E8 L$ y int main() { . 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#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 #define B 5 % E0 Z) f" r3 s/ k4 T0 {: r& ` #endif / ~ Y. \6 |8 U& ` int main() 7 ?3 [$ [& r! O" e { printf("%d", B); 1 M5 a. H6 R/ f" V k" R return 0; } 答案:B 解析:乍一看,输出似乎是编译时错误,因为尚未定义宏A,所以A是不等于3的,所以会将B定义为5。你如不信,也可以用上面的办法gcc -E hello.c -o hello.i来验证,或者编译运行一遍。 面试问题3#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 printf("客栈"); #endif int main() % i2 b# |6 v0 c+ ~ { return 0; }; F: \7 [) C, h" G 答案:C 编译错误 解析:程序编译三部曲:预处理、汇编、链接,那么在预处理时,上述代码就变成下面这样: #这里还有stdio.h的包含内容5 W( c# @: d4 X; Sprintf("客栈"); int main() {3 \: `8 D" l9 Q0 ~ return 0; } printf在main外面被调用了,所以编译会出错。 面试问题4#include <stdio.h> - Y/ m3 G! s1 A# Y8 y #define IS_EQUAL(X, Y) X == Y int main() % T7 Y: }6 [1 B* T { #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 printf("客栈"); # q b8 N& [+ p% a" Z: X #endif " `2 n: E. ]6 X return 0; } : ]. [& I2 d3 f( y 答案:A 解析:条件宏#if IS_EQUAL(X,0)扩展为#if X ==0。预处理结束后,所有未定义的宏均使用默认值0初始化。 面试问题5#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); 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 # include <stdio.h> # define scanf "%s Embedded Inn " int 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; } ! E; i0 _' t2 L( x( ]* D; m3 l 答案:D 解析:在编译的预处理阶段之后,printf语句将变为。 printf(“%s Embedded Inn”,“%s Embedded Inn”);面试问题7 #include <stdio.h> #define STR "嵌入式" int main() { / P" Z1 a: z& d9 Z( ^, R& `0 @. ? printf("%s",STR); #define STR "客栈" ) c3 y" b5 n7 z2 D6 ^% K9 f printf("%s",STR); return 0; . @2 {5 Y0 x& ?. g. _, a* b" W. a1 F } 答案:B 解析:如果重新定义预处理程序指令,则预处理器不会给出任何错误,它可能会发出警告。预处理器在使用之前获取新值,并将其替换。
#include<stdio.h> #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; printf("%d", ADHESION(var,1)); return 0; " L m% R! D! d4 h } " c0 w! n% s# w8 }5 ]2 J 答案:A 解析:运算符##称为“令牌粘贴(Token-Pasting)”或“合并(Merge)”运算符。它将两个符合合并为一个。因此在预处理之后,printf变为。 printf("%d", var1);面试问题9 #include <stdio.h> #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; } . Y! W, J! u( O1 |* q7 |$ Q* v 答案:C 解析:展开一看便知。 int main(){ float 6666.6 = 666.6; //常数不可为左值 printf("%d ", 6666.6);+ j* L5 t+ ~% C4 d return 0; } 面试问题10 #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() { , 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个比较典型的面试问题,相信对于宏理解不深的盆友会有些许帮助。 |
微信公众号
手机版