#ifdef __arm64__
extern int my_sub_machine(int a, int b);
extern int my_sub_assembly(int a, int b);
int result_machine = my_sub_machine(10, 2);
int result_assembly = my_sub_assembly(5, 3);
int result_c = my_sub_c(6, 2);
printf("Three results: %d, %d, %d\n", result_machine, result_assembly, result_c);
#endif
在隨后的幾年里,C語言的標準化委員會又不斷地對C語言進行改進,到了1999年,正式發布了ISO/IEC 9899:1999,簡稱為C99標準。C99標準引入了許多特性,包括內聯函數(inline functions)、可變長度的數組、靈活的數組成員(用于結構體)、復合字面量、指定成員的初始化器、對IEEE754浮點數的改進、支持不定參數個數的宏定義,在數據類型上還增加了long long int以及復數類型。毫不夸張地說,即便到目前為止,很少有C語言編譯器是完整支持C99的。像主流的GCC以及Clang編譯器都能支持高達90%以上,而微軟的Visual Studio 2015中的C編譯器只能支持到70%左右。
首先,不可移植性。每種處理器,其指令集都大相徑庭,比如ARM有ARM的指令集架構(ISA),Intel x86有x86的ISA,還有MIPS、Power(原來為PowerPC),Motorola 68000等;再加上各類微控制器單元(Micro-Controller Unit, MCU)、各類數字信號處理器(Digital Signal Processor, DSP),每種ISA都有其相應的匯編語言。那么多處理器如果對每一種都使用不同的匯編語言來實現同一個操作系統,那操作系統的開發人員真要崩潰了……而且即便實現出來,可能各個處理器上的實現也會有所不同,標準也很難被統一起來。
3)可延展性:大家或許已經知道,像微軟的Windows操作系統由數千名工程師合作研發;Linux操作系統對外開源,參與其中的研發人員也有數百上千人。如果我們在一個開發團隊中負責一個需要由多人合作開發的工程項目,那么我們寫的功能模塊需要與其他人寫的功能模塊進行對接。所以,我們在開發一個較大工程項目時,需要協調好各自對外的模塊接口(Application Program Interface, API)。由于C語言沒有全局名字空間(namespace)這個概念,所以命名一個對外接口也是非常重要的,否則可能會與其他功能模塊的接口名發生沖突。本書后面會對C語言函數命名以及符號連接做進一步介紹。
對于當前主流桌面操作系統而言,可使用Visual C++、GCC以及LLVM Clang這三大編譯器。其中,Visual C++(簡稱MSVC)只能用于Windows操作系統;其余兩個,除了可用于Windows操作系統之外,主要用于Unix/Linux操作系統。像現在很多版本的Linux都默認使用GCC作為C語言編譯器。而像FreeBSD、macOS等系統默認使用LLVM Clang編譯器。由于當前LLVM項目主要在Apple的主推下發展的,所以在macOS中,Clang編譯器又被稱為Apple LLVM編譯器。MSVC編譯器主要用于Windows操作系統平臺下的應用程序開發,它不開源。用戶可以使用Visual Studio Community版本來免費使用它,但是如果要把通過Visual Studio Community工具生成出來的應用進行商用,那么就得好好閱讀一下微軟的許可證和說明書了。而使用GCC與Clang編譯器構建出來的應用一般沒有任何限制,程序員可以將應用程序隨意發布和進行商用。不過由于MSVC編譯器對C99標準的支持就十分有限,加之它壓根不支持任何C11標準,所以本書的代碼例子不會針對MSVC進行描述。所幸的是,Visual Studio Community 2017加入了對Clang編譯器的支持,官方稱之為——Clang with Microsoft CodeGen,當前版本基于的是Clang 3.8。也就是說,應用于Visual Studio集成開發環境中的Clang編譯器前端可支持Clang編譯器的所有語法特性,而后端生成的代碼則與MSVC效果一樣,包括像long整數類型在64位編譯模式下長度仍然為4個字節,所以各位使用的時候也需要注意。為了方便描述,本書后面涉及Visual Studio集成開發環境下的Clang編譯器簡稱為VS-Clang編譯器。
而在嵌入式系統方面,可用的C語言編譯器就非常豐富了。比如用于Keil公司51系列單片機的Keil C51編譯器;當前大紅大紫的Arduino板搭載的開發套件,可用針對AVR微控制器的AVR GCC編譯器;ARM自己出的ADS(ARM Development Suite)、RVDS(RealView Development Suite)和當前最新的DS-5 Studio; DSP設計商TI(Texas Instruments)的CCS(Code Composer Studio);DSP設計商ADI(Analog Devices, Inc.)的Visual DSP++編譯器,等等。通常,用于嵌入式系統開發的編譯工具鏈都沒有免費版本,而且一般需要通過國內代理進行購買。所以,這對于個人開發者或者嵌入式系統愛好者而言是一道不低的門檻。不過Arduino的開發套件是可免費下載使用的,并且用它做開發板連接調試也十分簡單。Arduino所采用的C編譯器是基于GCC的。還有像樹莓派(Raspberry Pi)這種迷你電腦可以直接使用GCC和Clang編譯器。此外,還有像nVidia公司推出的Jetson TK系列開發板也可直接使用GCC和Clang編譯器。樹莓派與Jetson TK都默認安裝了Linux操作系統。在嵌入式領域,一般比較低端的單片機,比如8位的MCU所對應的C編譯器可能只支持C90標準,有些甚至連C90標準的很多特性都不支持。因為它們一方面內存小,ROM的容量也小;另一方面,本身處理器機能就十分有限,有些甚至無法支持函數指針,因為處理器本身不包含通過寄存器做間接過程調用的指令。而像32位處理器或DSP,一般都至少能支持C99標準,它們本身的性能也十分強大。而像ARM出的RVDS編譯器甚至可用GNU語法擴展。
圖1-1展示了上述C語言編譯器的分類。
圖1-1 C語言編譯器的分類
1.4 關于GNU規范的語法擴展
GNU是一款能用于構建類Unix操作系統的計算機軟件合集,由自由軟件之父Richard Stallman開創,于1983年9月27日對外發布。GNU完全由自由軟件(free software)構成。GNU語法擴展源自于GCC編譯器,在1987年發布1.0版本,稱為GNU C Compiler。隨后,GCC編譯器前端 支持了C++、Objective-C/C++、Fortran、Ada、Java以及最近躍升的Go等編程語言,因此現在GCC被稱為GNU Compiler Collection。由于在20世紀90年代,GNU C編譯器就對C90標準做了相當多的語法擴展,包括復合字面量、匿名結構體和數組、可指定的初始化器等,這些語法擴展被廣泛使用,尤其是大量用于Linux內核代碼中,因此C99標準將這些語法特性全都列入標準之中。