C Preprocessor系列,其實就是CPP manual的讀書心得。
initial processing 在初始化的處理上,CPP主要有四個任務:
- 讀取檔案到記憶體中。
- 如果有啟動trigraphs,那麼會做trigraph轉換,如??/轉換成\,??-轉成~等等。
- 合併連續行成一行,就是每行後面有\當結尾的,會將該行和下一行合併成一行。
- 將註解的部分用一個空白取代。
brook@debian:~$ echo -e "#define foo() bar\nfoo()baz"|gcc -E - # 1 "<stdin>" # 1 "<built-in>" # 1 "<command-line>" # 1 "<stdin>" bar baz
The preprocessing language 所謂的preprocessing language,就是以#開頭的那些preprocessing directive,這些指令是固定的,無法讓使用者自訂新的。這些指令主要有四種用途:
- include of header file(比如#include)。
- Macro expansion(比如將#define展開)。
- Conditional Compilation(比如#if,決定哪些要被編譯,哪些不被編譯)。
- Line control(我不是很清楚,只知道#line num下一行就會由num當行數重新計算,也可以簡寫成# num)。
- Diagnostic(比如#error)。
Header files Header files主要分成兩類:
- System header files:提供OS部分的interface,<xx.h>。
- Your own header file:提供source files的interface,"xx.h"。
brook@desktop:~$ cat a.h #ifndef A_H #define A_H int hello(char *str); #endif brook@desktop:~$ cat a.c #include看到把a.h中的int hello(char *str)複製到source file中了嗎?隨後又重新計算行數# 3,而header files並沒有規定只能放啥東西,不過通常就是放置一些宣告。 include syntax #include有兩種變形:#include "a.h" int main(int argc, char * argv[]) { printf("hello world\n", __LINE__); return 0; } brook@desktop:~$ cpp -I./ a.c ... 略 ... # 2 "a.c" 2 # 1 "a.h" 1 int hello(char *str); # 3 "a.c" 2 int main(int argc, char * argv[]) { printf("hello world\n", 6); return 0; }
- #include <file>:for system header files,就是會尋找系統路徑。
- #include "file":for your own header files,會先尋找目前目錄,找不到就依循
的search path尋找。
search path 一般在UNIX底下,system header files的search path為 /usr/local/include libdir/gcc/target/version/include /usr/target/include /usr/include 透過GCC實際觀察一下吧
brook@desktop:~$ gcc -I./ -v a.c ... 略 ... ignoring nonexistent directory "/usr/local/include/i486-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.3.2/../../../../i486-linux-gnu/include" ignoring nonexistent directory "/usr/include/i486-linux-gnu" #include "..." search starts here: #include <...> search starts here: ./ /usr/local/include /usr/lib/gcc/i486-linux-gnu/4.3.2/include /usr/lib/gcc/i486-linux-gnu/4.3.2/include-fixed /usr/include End of search list. ... 略 ...您可以透過-I新增search path,新增的search path會優先被尋找,但是如果新增的search path已經在預設的(系統的)search path中,該search path會被忽略,避免影響預設的search path順序。有興趣可以再研究一下-I-和-iquote。
Once-Only headers 一個header files很可能被include兩次,進而造成重複define,引起compile error,標準做法應該是在header file中,使用conditional compilation防止被引入兩次,如:
#ifndef XX_H #define XX_H the entire file #endif /* !XX_H */XX_H我們稱為controlling macro或guard macro。macro name不應該使用'_'開頭的命名方式,而在system header files中應該使用'__'開頭,避免與user program造成衝突。 computed include 有時候我們會需要根據不同的configure引入不同的header files,比如:
#if SYSTEM_1 #include "system1.h" #elif SYSTEM_2 #include "system2.h" #elif ... 略 ... #endif像這樣需要經過計算然後才引入的行為就稱為computed include,你可以發現很就會被許多的#elif淹沒,你可以使用簡單的方式替代,比如:
#define SYSTEM1_H <stdio.h> #define SYSTEM2_H "system3.h" ... 略 ... #include SYSTEM1_H #include SYSTEM2_H甚至SYSTEM_H可以由Makefile傳遞進來。
System Headers是用來宣告OS和runtime libraries的interface,system header所產生的warning除了#warning會顯示出來,其他的都會被抑制住。一般而言,當GCC在編譯時就已經設定哪些目錄會被當成system header存放的目錄了,不過我們還是可以透過-isystem和#pragma GCC system_header兩種方式,將一般的header file當成system header file。
- -isystem後面接的目錄會被當成system header,用法上和-I一樣。
- #pragma GCC system_header這個指令告訴GCC把這個header file當成system header。
brook@ubuntu:~$ cat syshdr.c #include <stdio.h> #include "syshdr1.h" #include "syshdr2.h" #include "syshdr3.h" int main(int argc, char *argv[]) { syshdr1(); syshdr2(); syshdr3(); return 0; } brook@ubuntu:~$ cat syshdr/syshdr1.h #ifndef SYSHDR1_H #define SYSHDR1_H int syshdr1(void) { printf("%s\n", __FUNCTION__); } #endif brook@ubuntu:~$ cat syshdr2.h #ifndef SYSHDR2_H #define SYSHDR2_H #pragma GCC system_header int syshdr2(void) { printf("%s\n", __FUNCTION__); } #endif brook@ubuntu:~$ cat syshdr3.h #ifndef SYSHDR3_H #define SYSHDR3_H int syshdr3(void) { printf("%s\n", __FUNCTION__); } #endif brook@ubuntu:~$ gcc -Wall -isystem syshdr syshdr.c -o my_syshdr syshdr.c: In function ‘syshdr3’: syshdr3.h:3: warning: control reaches end of non-void function
http://c-faq.com/decl/auto.html
回覆刪除What's the auto keyword good for?
The auto keyword is useless in the C language. It is there because before the C language there existed a B language in which that keyword was necessary for declaring local variables. (B was developed into NB, which became C).
https://stackoverflow.com/questions/2192547/where-is-the-c-auto-keyword-used
http://c-faq.com/decl/extern.html
回覆刪除What does extern mean in a function declaration?
extern is significant only with data declarations. In function declarations, it can be used as a stylistic hint to indicate that the function's definition is probably in another source file, but there is no formal difference between
extern int f();
and int f();
http://c-faq.com/decl/typedefvsdefine.html
回覆刪除What's the difference between using a typedef or a #define for a user-defined type?
In general, typedefs are preferred, in part because they can correctly encode pointer types. For example, consider these declarations:
typedef char *String_t;
#define String_d char *
String_t s1, s2;
String_d s3, s4;
s1, s2, and s3 are all declared as char *, but s4 is declared as a char, which is probably not the intention.