C語言的前置處理(CPP: C preprocessor)是個很有趣的題目,知道在對的時機使用他們絕對能夠對於開發軟體有巨大的幫助。以下是我閱讀GNU: The C Preprocessor的筆記。請注意這只是筆記,詳細資料請自行參考原本網頁。
Ubuntu 12.04
-std=c90, -std=c99或-std=c11參數檢查。全開就用-pedantic參數-finput-charset=參數指定source file 字元編碼??/ 代表 \a+++++b -> a++ ++ +b而不是a++ + ++b"中間包含的字串,包括#include "test.h"#define MY() ddd
int main(void)
{
int i,MY()__;
return
}
展開後就是
## 1 "b.c"
## 1 "<built-in>"
## 1 "<command-line>"
## 1 "b.c"
int main(void)
{
int i,ddd __;
return
}
包含指令(directives)和巨集(macros)
提供
除了gcc內建的directives外,directives以#開頭,前後都可以有空白
#include效果和複製header file到原始碼檔案一樣。你可以用gcc -E 原始碼檔名看到header file被放入。之所以這樣做就是讓使用者省去複製和多次修改相同介面的時間。#include <file>
-I參數指定標頭檔路徑#include "file"
-iquote指定的目錄-I指定的目錄echo | gcc -v -x c -E -可以列出gcc 搜尋標頭檔目錄-I參數,一個以上-I路徑則由左至右開始搜尋-nostdinc讓gcc不去搜尋系統標準的include path#ifndef MY_HEADER_H
#define MY_HEADER_H
#define MY_VAR (1)
#endif /* MY_HEADER_H */
#ifdef LINUX
#include <linux_plat.h>
#else
#include <windows_plat.h>
#endif
#include MY_DEF_H
CFLAGS=-DMY_DEF_H="<linux_plat.h>"
GCC會忽略掉系統標頭檔的Warning,如果其他一般標頭檔想要有相同的處理方式,可以使用-isystem 路徑讓GCC將該目錄下的標頭檔視為系統標頭導
#define foo(): 重點是(),()前面不得有空格
callback = foo;這樣的描述,分開。所以MAC(buf[x = 1, u + 1])這樣的程式碼會被拆成兩個參數,請使用MAC((buf[x = 1, u + 1])),隔開。#define MY_MAC(x,y) my_mac(x,y)
...
MY_MAC(,);
MY_MAC(a,b);
MY_MAC(a,);
MY_MAC(,b);
"並且和參數相同,最後並不會被替換。
FOO(X) _bar(x, "x"); -> FOO(bar) _bar(bar, "x");#define ENCLOSE_QUOTE(VAR) #VAR
ENCLOSE_QUOTE(test); -> "test"#define PLAY 0
#define ENCLOSE_QUOTE(VAR) #VAR
#define TO_STR(VAR) ENCLOSE_QUOTE(VAR)
ENCLOSE_QUOTE(PLAY); -> "PLAY"TO_STR(PLAY) -> ENCLOSE_QUOTE(0) -> "0"
echo -e "#define MYDEF(PARAM1, PARAM2) PARAM1##PARAM2 \n\n MYDEF(xxx, yyy)" | gcc -E -x c -
#define my_printf(...) printf("myprintf: " __VA_ARGS__)#define my_printf(arg...) printf("myprintf: " args)#define my_printf(format, ...) fprintf(stderr, format, __VA_ARGS__)
my_printf("test");會GG,因為被轉成fprintf(stderr, "test",);#define my_printf(format, ...) fprintf(stderr, format, ##__VA_ARGS__)
, 硬食掉__FILE____LINE____func__
__FUNCTION____DATE____TIME____STDC__:顯示目前是否使用Standard C編譯程式碼__STDC_VERSION__:Standard C版本__cplusplus:是否為C++編譯環境__OBJC__:是否為object C編譯環境__ASSEMBLER__:是否在組合語言環境__COUNTER__:回傳一個從0開始的數字,每次呼叫一次加一__VERSION____GNUC____GNUC_MINOR____GNUC_PATCHLEVEL____GNUG__:是否是用GNU C++__INCLUDE_LEVEL__:標頭檔被引用的深度,從0開始算。如a->b->c,c就是2__ELF__:輸出格式是否為elf__TIMESTAMP__echo "" | gcc -x c -E -dM -__包夾或是_+大寫為系統內建Macro,其name space保留。#define FOO BAR
...
#undef FOO
#define FOO BAR2
#define MY_PRT(X) printf(x)
#define P_TEST(M) M("test")
...
P_TEST(MY_PRT);
#define TEST(x) func1(x, "test"
...
test(my_param));
#define MY_TEST(x) { printf("test\n"); }
...
if (bit)
MY_TEST(bit);
else
printf("else\n");
編譯會錯誤,原因是因為分號會讓gcc判斷if statement已經結束
cc test.c -o test
test.c: In function ‘main’:
test.c:23:5: error: ‘else’ without a previous ‘if’
make: *** [test] Error 1
do {...} while (0)#define MY_TEST(x) do { printf("test\n"); } while(0)
...
if (bit)
MY_TEST(bit);
else
printf("else\n");
除非明確瞭解巨集內容,否則避免直接傳函數進去巨集。MY_TEST(func(xy,xx), 10));
-I /usr/include)或是直接連在一起(-I/usr/include)-D 文字: 定義文字巨集為1
echo "TEST" | gcc -E -xc -DTEST --D 文字=文字:定義文字巨集
echo "TEST" | gcc -E -xc -DTEST=CPP --U: undefine 巨集,如果參數同時有-U和-D的話優先先出現的先做-I dir:指定搜尋header file目錄
dir以=開頭,gcc會取代成sysroot路徑,參考--sysroot和-isysroot-M:列出要檔案要吃的source code和header files
$ gcc -M b.c
b.o: b.c /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdint.h \
/usr/include/stdint.h /usr/include/features.h \
/usr/include/x86_64-linux-gnu/bits/predefs.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/bits/wchar.h /usr/include/stdio.h \
/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h b.h
-MM:列出要檔案要吃的source code和非系統的header files$ gcc -MM b.c
b.o: b.c b.h
-MF file:配合-M, -MM時將結果寫入file-MG: -M, -MMparse到header file不存在會跳錯誤,而加上-MG後會假裝他們存在,照樣吐出結果。$ mv b.h a
$ gcc -MM b.c
b.c:3:15: fatal error: b.h: No such file or directory
compilation terminated.
$ gcc -MG -MM b.c
b.o: b.c b.h
-MP: 順便把depend的target加入$ gcc -MP -MM b.c
b.o: b.c b.h
b.h:
-MT :指定target$ gcc -MT test -MM b.c
test: b.c b.h
-MD: 直接編譯,同時把會吃的檔案和header files放在*.d檔$ rm a.out
$ gcc -MD b.c
$ ls a.out
a.out
$ cat b.d
b.o: b.c /usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdint.h \
/usr/include/stdint.h /usr/include/features.h \
/usr/include/x86_64-linux-gnu/bits/predefs.h \
/usr/include/x86_64-linux-gnu/sys/cdefs.h \
/usr/include/x86_64-linux-gnu/bits/wordsize.h \
/usr/include/x86_64-linux-gnu/gnu/stubs.h \
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
/usr/include/x86_64-linux-gnu/bits/wchar.h /usr/include/stdio.h \
/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stddef.h \
/usr/include/x86_64-linux-gnu/bits/types.h \
/usr/include/x86_64-linux-gnu/bits/typesizes.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/lib/gcc/x86_64-linux-gnu/4.6/include/stdarg.h \
/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h b.h
-MMD: 直接編譯,同時把會吃的檔案和非系統header files放在*.d檔$ rm a.out
$ gcc -MMD b.c
$ ls a.out
a.out
$ cat b.d
b.o: b.c b.h
-dM: 列出編譯時所有的巨集,不列出展開巨集的結果, 通常配合-E
-dD: 列出編譯時不包含predefine的巨集以及展開巨集的結果, 通常配合-E
-dN: 和-dD的差別是巨集只列出名稱不列出要展開的內容, 通常配合-E
#define foo bar -dD輸出 #define foo bar,-dN結果 #define foo-dI:不顯是巨集,而是顯示#include的指令
-C: 保留註解,不包含巨集展開的部份註解
-CC: 保留註解,包含巨集展開的部份註解
-H:列出編譯時會參考的header files
#include <stdint.h>
#include <stdio.h>
#include "b.h"
#define foo (bit,lfsr) /* XXX */
#define bar(x) lose(x)
#define lose(x) (1 + (x))
#if aaa
#define ddd ddds
#endif
int main(void)
{
uint16_t lfsr = time(0);
unsigned bit;
unsigned period = 0;
int c= 644;
printf("%s\n", __BASE_FILE__);
printf("%s\n", __VERSION__);
if (bit)
MY_TEST(bit);
else
MY_TEST(bit);
printf("%d\n", (lfsr,bit,c));
printf("%d\n", (bit, lfsr));
#line 10
printf("test:%d, %s\n", __LINE__, __FILE__);
#if 0
lfsr = typeof(bit) 10;
#endif
bar(foo);
printf("test:%d, %s\n", __LINE__, __FILE__);
return 0;
}
#define MY_TEST(x) do { printf("test\n"); } while(0)