[收藏] 宏工作原理以及典型面試10問
掃描二維碼
隨時隨地手機看文章
宏工作原理
以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;
}
上面這步操作做了三件事情:
-
刪除注釋:刪除所有注釋。注釋僅供程序員理解代碼,注釋對機器沒有用。因此預處理器在預處理過程中會刪除注釋,因為注釋在執(zhí)行過程中是不需要的,也不會被執(zhí)行。所以注釋盡管寫不影響程序的邏輯,當然寫的過也未必是好事,過少也不是好事。個人理解一份好的代碼應盡量少注釋,應該通過合理的命名習慣,良好的編程風格來提高可讀性,在一些關鍵復雜算法處則應清晰的加上注釋。在hello.c中的注釋 /*這是一個hello word程序*/ 在預處理后被刪除掉了。
-
文件包含:包含程序需要的所有文件。C語言中使用#include,這是預處理器的指令,告訴預處理器包含指定文件的內容。例如#include將告訴預處理器將stdio.h中所有的內容包含進來。也可以使用雙引號-#include “stdio.h” 注意:如果使用尖括號,則在編譯器包含路徑中搜索文件。如果文件名用雙引號包起來,則搜索路徑將擴展為除了編譯器包含路徑外的當前目錄下。
-
宏展開替換:比如上例中宏STR在預處理時就被展開替換了。宏有兩種常見形式:
大致說明了宏的工作原理,來看看一些常見的面試問題:
-
不帶參形式(有的地方也稱對象形式object-like)。
#define PI 3.1415926f
-
帶參形式(有的地方也稱為函數(shù)形式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("嵌入式客棧\n"); \
continue; \
} \
}while(1)
int main()
{
PRINT_HELLO(0, 3);
return 0;
}
答案:D
解析:PRINT_HELLO宏在預處理器時被擴展。宏展開后,if表達式變?yōu)椋篿f(0 ++ <3)。0是一個常數(shù),常數(shù)如何自增呢?,因此應用增量運算符會產生編譯時錯誤。
面試問題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),表達式變?yōu)?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語句將變?yōu)椤?/p>
printf(“%s Embedded Inn”,“%s Embedded Inn”);
面試問題7
下述代碼的輸出應該是?
(A) 編譯錯誤
(B) 嵌入式客棧
(C) 客??蜅?/p>
(D) 嵌入式嵌入式
#include <stdio.h>
#define STR "嵌入式"
int main()
{
printf("%s",STR);
#define STR "客棧"
printf("%s",STR);
return 0;
}
答案:B
解析:如果重新定義預處理程序指令,則預處理器不會給出任何錯誤,它可能會發(fā)出警告。預處理器在使用之前獲取新值,并將其替換。
面試問題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變?yōu)椤?/p>
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; //常數(shù)不可為左值
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個比較典型的面試問題,相信對于宏理解不深的盆友會有些許幫助。
如喜歡請點贊/在看/分享支持!
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!