閱讀源碼時有很多的條件編譯語句,看起來不爽,怎麼辦?

    有些C程序,在編寫的時候,爲了應對多種情況(比如說支持多平臺,選擇某些特性等) ,就在源碼當中使用了很多的宏來控制。當某些宏打開的時候某些代碼才生效。如果代碼當中,這些宏比較少,那還沒什麼,但是當代碼當中有極多的宏的時候,閱讀起來就很頭疼了。如果有朋友看過QEMU的源碼,會深有同感的。它的源碼當中定義了很多的宏,有些函數針對某個平臺的代碼並不長,但是爲了支持多平臺,它就要定義不同的宏來控制在不同平臺上對應的代碼起作用。這對編譯器是好事,但是如果我們只想看某個平臺相關的代碼就很麻煩了,以前很是苦惱,找不到很好的解決這個問題的辦法。
    最近在用GCC的時候,發現其中的一些命令選項可以幫助我解決這個問題。基本的思想是讓GCC在編譯的時候不刪除臨時文件,而這些臨時文件就包含了經過預處理之後的代碼,這時候就不會有宏的干擾了,但是也失去了宏原本的意義(比如說某些常量,使用宏的話,可以見名知義)。舉個例子來說,我們有這麼一個C程序a.c
#include <stdio.h>
int main(int argc, char *argv[])
{
    
int arch;
#ifdef _I386_
    arch 
= 1;
#elif _MIPS_
    arch 
= 2;
#elif _ARM_
    arch 
= 3;
#else
#error "Give me a architecture select!"
#endif
    
return 0;
}

    如果我們輸入命令: $ gcc -D_MIPS_  -save-temps a.c
    這時候-save-temps選項告訴gcc在編譯的過程中不要刪除臨時文件,那麼在當前文件夾下就會產生:
1)a.i : 是經過C預處理程序(CPP)處理之後的文件,這個就是我們以後工作的基礎
2)a.s: 是經過彙編器處理之後產生的彙編代碼
3)a.o: 是編譯器產生的目標代碼
4)a.out: 是編譯器默認產生的可執行程序。
    我們主要在a.i的上面進行操作,如果我們想看MIPS平臺相關的代碼,我們就使用上面的命令產生a.i,我們來看a.i的內容, 由於內容比較多,我這裏只給出前10行和主體代碼部分:
1 "a.c"
1 "<built-in>"
1 "<command line>"
1 "a.c"
1 "/usr/include/stdio.h" 1 3 4
28 "/usr/include/stdio.h" 3 4
1 "/usr/include/features.h" 1 3 4
329 "/usr/include/features.h" 3 4
1 "/usr/include/sys/cdefs.h" 1 3 4
313 "/usr/include/sys/cdefs.h" 3 4

...............................

850 "/usr/include/stdio.h" 3 4

2 "a.c" 2
int main(int argc, char *argv[])
{
    
int arch;



    arch 
= 2;





    
return 0;
}
    可以看出來,文件的內容基本是這麼幾個組成部分:
1) #include 語句會被擴展開來,並把對應內容包含進來,同時加入 “# _line_number "_include_file_name_
" xx xx" 這些信息
2) 源文件當中的信息會有所保留,宏被展開,註釋被換成空行。條件宏起作用,其所在的行被換成空行。
    我們要做的就是把這些以#號開頭的行和所有空行刪除,並重新對a.i文件進行排版,那麼它成了一個我們希望要的經過宏展開之後的C文件。
    如果文件數目不多,我們可以使用這個命令 $ gcc -D_MIPS_ -E -P a.c >t.c 來實現上述功能,這個命令就是告訴GCC只進行預編譯,並且在預編譯的時候不產生“#include"語句和一些不必要的空行。但是當文件很多,特別是我們使用Makefile文件來控制我們整個工程的編譯的時候,這個方法就不合適了,就需要使用我前面提到的第
一種方法,具體做法如下:
1) 在Makfile文件當中的CFLAGS定義你想要的宏,並加上-save-temps選項,對於我們這個例子,我們可以定義: CFLAGS +=-D_MIPS -save-temps,然後執行make命令。執行成功之後,會生成一堆的 .i文件,下一步我麼要操作.i文件。
2)執行這麼一個腳本,它的作用就是把當前目錄下的所有以.i作爲後綴名的文件當中的 “#include" 行和空行刪除。
#!/bin/bash
#filename: clearify.sh

for f in ./*.i
do
    name
=`basename $f`
    sed 
"/^[[:space:]]*$/d" $name >t1
    sed 
"/^# .*$/d" t1 >$name
done
如果你覺得文件排版不好看,還可以使用 $ indent  -kr  file_name 來進行美化排版。

希望對大家閱讀含有很多宏的源碼時有所幫助。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章