當(dāng)前位置:首頁 > 公眾號精選 > C語言與CPP編程
[導(dǎo)讀][導(dǎo)讀] C語言中宏是非常有價(jià)值的語言特性之一,也是面試中必考察的要點(diǎn)之一,本文來分享總結(jié)一些關(guān)于宏的常見面試問題。 希望能幫助到有需要的小伙伴們。 宏工作原理 以hello word程序?yàn)槔齺砜纯?將下述代碼存成hello.c #include? #define?STR?"hell

[導(dǎo)讀] C語言中宏是非常有價(jià)值的語言特性之一,也是面試中必考察的要點(diǎn)之一,本文來分享總結(jié)一些關(guān)于宏的常見面試問題。 希望能幫助到有需要的小伙伴們。

宏工作原理

以hello word程序?yàn)槔齺砜纯?將下述代碼存成hello.c

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

為了說明問題,這里用下面的命令進(jìn)行顯式預(yù)處理,將得到hello.i文件,實(shí)際編譯過程中,會自動完成。

//gcc -E 生成預(yù)處理文件
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;
}

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

  • 刪除注釋:刪除所有注釋。注釋僅供程序員理解代碼,注釋對機(jī)器沒有用。因此預(yù)處理器在預(yù)處理過程中會刪除注釋,因?yàn)樽⑨屧趫?zhí)行過程中是不需要的,也不會被執(zhí)行。所以注釋盡管寫不影響程序的邏輯,當(dāng)然寫的過也未必是好事,過少也不是好事。個(gè)人理解一份好的代碼應(yīng)盡量少注釋,應(yīng)該通過合理的命名習(xí)慣,良好的編程風(fēng)格來提高可讀性,在一些關(guān)鍵復(fù)雜算法處則應(yīng)清晰的加上注釋。在hello.c中的注釋  /*這是一個(gè)hello word程序*/  在預(yù)處理后被刪除掉了。

  • 文件包含:包含程序需要的所有文件。C語言中使用#include,這是預(yù)處理器的指令,告訴預(yù)處理器包含指定文件的內(nèi)容。例如#include將告訴預(yù)處理器將stdio.h中所有的內(nèi)容包含進(jìn)來。也可以使用雙引號-#include “stdio.h” 注意:如果使用尖括號,則在編譯器包含路徑中搜索文件。如果文件名用雙引號包起來,則搜索路徑將擴(kuò)展為除了編譯器包含路徑外的當(dāng)前目錄下。

  • 宏展開替換:比如上例中宏STR在預(yù)處理時(shí)就被展開替換了。宏有兩種常見形式:

    大致說明了宏的工作原理,來看看一些常見的面試問題:

    • 不帶參形式(有的地方也稱對象形式object-like)。

      #define PI 3.1415926f
    • 帶參形式(有的地方也稱為函數(shù)形式function-like)。

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

面試問題1

如下代碼,問有多少個(gè)"嵌入式客棧"會被打???

(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宏在預(yù)處理器時(shí)被擴(kuò)展。宏展開后,if表達(dá)式變?yōu)椋篿f(0 ++ <3)。0是一個(gè)常數(shù),常數(shù)如何自增呢?,因此應(yīng)用增量運(yùn)算符會產(chǎn)生編譯時(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

解析:乍一看,輸出似乎是編譯時(shí)錯誤,因?yàn)樯形炊x宏A,所以A是不等于3的,所以會將B定義為5。你如不信,也可以用上面的辦法gcc -E hello.c -o hello.i來驗(yàn)證,或者編譯運(yùn)行一遍。

面試問題3

問:針對下述代碼,哪個(gè)答案正確?

(A) 嵌入式

(B) 客棧 

(C) 編譯錯誤

(D) 運(yùn)行錯誤

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

    return 0
}

答案:C 編譯錯誤

解析:程序編譯三部曲:預(yù)處理、匯編、鏈接,那么在預(yù)處理時(shí),上述代碼就變成下面這樣:

#這里還有stdio.h的包含內(nèi)容
printf("客棧"); 
int main()
{
    return 0;
}

printf在main外面被調(diào)用了,所以編譯會出錯。

面試問題4

下述代碼的輸出應(yīng)該是?

(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)擴(kuò)展為#if X ==0。預(yù)處理結(jié)束后,所有未定義的宏均使用默認(rèn)值0初始化。

面試問題5

下述代碼的輸出應(yīng)該是?

(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

解析:預(yù)處理器用10*10替換SQUARE(10),表達(dá)式變?yōu)?x = 2000/10 * 10,x的值計(jì)算為2000。如前所說,應(yīng)定義如下:

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

面試問題6

下述代碼的輸出應(yīng)該是?

(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(scanfscanf); 
   return 0

答案:D

解析:在編譯的預(yù)處理階段之后,printf語句將變?yōu)椤?/p>

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

面試問題7

下述代碼的輸出應(yīng)該是?

(A) 編譯錯誤

(B) 嵌入式客棧 

(C) 客??蜅?/p>

(D) 嵌入式嵌入式

#include <stdio.h> 
#define STR "嵌入式" 
int main() 

  printf("%s",STR); 
  
  #define STR "客棧" 
  
  printf("%s",STR);
  return 0
}

答案:B

解析:如果重新定義預(yù)處理程序指令,則預(yù)處理器不會給出任何錯誤,它可能會發(fā)出警告。預(yù)處理器在使用之前獲取新值,并將其替換。

面試問題8

下述代碼的輸出應(yīng)該是?

(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

解析:運(yùn)算符##稱為“令牌粘貼(Token-Pasting)”或“合并(Merge)”運(yùn)算符。它將兩個(gè)符合合并為一個(gè)。因此在預(yù)處理之后,printf變?yōu)椤?/p>

printf("%d", var1); 

面試問題9

下述代碼的輸出應(yīng)該是?

(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

下述代碼的輸出應(yīng)該是?

(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,認(rèn)為將MAIN敲成大寫了,其實(shí)仔細(xì)一看,通過前面兩個(gè)宏以及粘連操作符##將MAIN替換成main,所以沒有問題,這個(gè)題目比較騷,主要考察細(xì)心以及粘連操作符。

總結(jié)一下

面試小提示:實(shí)際筆試中,只有掌握了宏的基本操作原理,以及宏預(yù)處理的本質(zhì),在解題時(shí)細(xì)心展開,一般而言不會有什么問題。

本文總結(jié)了宏的基本工作原理,以及10個(gè)比較典型的面試問題,相信對于宏理解不深的盆友會有些許幫助。

如喜歡請點(diǎn)贊/在看/分享支持!

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉