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

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

宏工作原理

以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(scanfscanf); 
   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)系我們,謝謝!

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