Android NDK 1 C語言基礎

一、C語言的起源

C 語言是貝爾實驗室的 Dennis Ritchie 于 1969 年 ~ 1973 年間創(chuàng)建的。美國國家標準學會(ANSI)在 1989 年頒布了 ANSI C 標準,后來 C 語言的標準化成了國際標準化組織(ISO)的責任。這些標準定義了 C 語言和一系列函數(shù)庫,即 C 標準庫。

  • C 語言與 Unix 的關系

C 語言從一開始就是作為一種用于 Unix 系統(tǒng)的程序語言開發(fā)出來的。大部分 Unix 內(nèi)核,以及所有支撐工具和函數(shù)庫都是用 C 語言編寫的。

  • C 語言小而簡單

C 語言的設計是由一個人而非一個協(xié)會掌控的,它是一個簡潔明了、沒有什么冗贅的設計。C 語言經(jīng)典著作 《The C Programming Language》(K & R)中包含大量例子和練習描述了完整的 C 語言及其標準庫,而全書還不到 300 頁。

  • C 語言是為實踐目的設計的

二、C 語言概述

C 語言目前最新版本由 ISO/IEC 9899:2011 定義。當前版本一般稱為 C11,但是 C11 中一些語言元素是可選的,這就意味著遵循 C11 的編輯并沒有實現(xiàn)該變準中的所有功能。

C11 標準是 ISO/IEC 9899:2011 - Information technology -- Programming languages -- C 的簡稱,曾用名為 C1X。

C11 標準是 C 語言標準的第三版,前一個標準版本是 C99 標準。2011年12月8日,國際標準化組織(ISO)和國際電工委員會(IEC) 旗下的C語言標準委員會(ISO/IECJTC1/SC22/WG14)正式發(fā)布了 C11 標準。

2.1 標準庫

C 標準庫也在 C11 標準中指定,標準庫定義了編寫 C 程序時常用的常量、符號和函數(shù),還提供了一些 C 語言的一些可選擴展。標準庫在一系列標準文件:頭文件(.h)中指定。

三、創(chuàng)建 C 程序

C 程序的創(chuàng)建由以下四個步驟:

  1. 編輯

  2. 編譯

  3. 鏈接

  4. 執(zhí)行

下面就來詳細分析每個步驟:

3.1、編輯

編輯過程就是創(chuàng)建和修改 C 程序的源代碼。

3.2、編譯

編譯器將源代碼轉(zhuǎn)換為機器語言,在編譯過程中會找出并報告錯誤。該階段的輸入是在編輯期產(chǎn)生文件(源文件)。編譯器的輸出結果稱為對象代碼(object code),存放它們的文件稱為對象文件(object file),在 Linux/Unix 通常是.o。

下面是一個在 Linux 下編譯的示例:

hello.c 源文件:

#include <stdio.h>

int main()
{
    printf("hello,world\n");
    return 0;
}

編譯指令:

cc -c hello.c  或 gcc -c hello.c

gcc 指令的一般格式為:gcc [選項] 要編譯的文件 [選項] [目標文件]

以上指令中 hello.c 是需要編譯的源文件,生成的文件為 hello.o;如果省略了 -c 這個參數(shù),那么程序還會自動鏈接,生成的文件默認為 a.out。

編譯部分分為三個階段:

  1. 預處理階段

該階段會修改或添加代碼,預處理器(cpp)會根據(jù)以字符 # 開頭的命令,修改原始的 C 程序。比如在程序中第一行為 #include <stdio.h>,那么該命令告訴預處理器讀取頭文件 stdio.h 的內(nèi)容,并把它直接插入程序文本中。結果就得到另一個 C 程序,通常以 .i 作為文件擴展名。

指令如下:

    gcc -E hello.c -o hello.i
  1. 編輯階段

該階段是生成對象代碼的實際編譯過程,編譯器(ccl)將 .i 文件翻譯成文本文件(文件擴展名為 .s),它包含一個匯編語言程序

在這個階段中,gcc 首先要檢查代碼的規(guī)范性、是否有語法錯誤等,以確定代碼的實際要做的工作,在檢查無誤后,gcc 把代碼翻譯成匯編語言。用戶可以使用“-S”選項來進行查看,該選項只進行編譯而不進行匯編,生成匯編代碼。匯編語言是非常有用的,它為不同高級語言不同編譯器提供了通用的語言。

如:C 編譯器和 Fortran 編譯器產(chǎn)生的輸出文件用的都是一樣的匯編語言。

指令如下:

gcc -S hello.i -o hello.s

hello.s 文件內(nèi)容如下:

          .file "hello.c"
          .section  .rodata
  .LC0:
          .string   "hello,world"
          .text
          .globl    main
          .type main, @function
  main:
  .LFB0:
          .cfi_startproc
          pushq %rbp
          .cfi_def_cfa_offset 16
          .cfi_offset 6, -16
          movq  %rsp, %rbp
          .cfi_def_cfa_register 6
          leaq  .LC0(%rip), %rdi
          call  puts@PLT
          movl  $0, %eax
          popq  %rbp
          .cfi_def_cfa 7, 8
          ret
          .cfi_endproc
  .LFE0:
          .size main, .-main
          .ident    "GCC: (Ubuntu 7.2.0-8ubuntu3) 7.2.0"
          .section  .note.GNU-stack,"",@progbits
  1. 匯編階段

接下來匯編器(as)將 .s 文件翻譯成機器語言指令,把這些指令打包成一種叫做可重定位目標程序(relocatable object program)的格式,并將結果保存在 .o 文件中,該文件是一個二進制文件。

指令如下:

    gcc –c hello.s –o hello.o

3.3、鏈接

鏈接器(ld)將原代碼文件中由編譯器產(chǎn)生的各種對象模塊組合起來,再從 C 語言提供的程序庫中添加必要的代碼模塊,將他們組合成一個可執(zhí)行文件。鏈接器也可以檢測和報告錯誤。如果連接成功會產(chǎn)生一個可執(zhí)行文件,在 Linux/Unix 環(huán)境下,該文件無擴展名。

指令如下:

gcc hello.o -o hello

以上幾個步驟可以使用以下指生成可執(zhí)行文件:

gcc -o hello hello.c

3.4、執(zhí)行

經(jīng)過以上幾個步驟,hello.c 已經(jīng)被編譯系統(tǒng)翻譯成成了可執(zhí)行目標文件 hello,并被保存在磁盤上。在 Linux/Unix 系統(tǒng)下只要在終端輸入可執(zhí)行文件名就可以執(zhí)行程序。

指令:

linux> ./hello
hello,world

以上步驟可以總結為下圖:

源文件轉(zhuǎn)化為目標文件.png

四、示例

使用前面的 hello.c 作為示例:

#include <stdio.h>

int main()
{
    printf("Hello world!\n"); // print
    return 0;
}

4.1、注釋

C語言的注釋分為兩種:

  1. 以“//”開頭的;

  2. 在“/** **/”之間的。

4.2、預處理指令

以下代碼行:

#include <stdio.h>

嚴格來說他并不是可執(zhí)行程序的一部分,但是它很重要,程序缺少它是不可以執(zhí)行的。符號“#”表示這是一個預處理指令,告訴編譯器在編譯源代碼之前,要先做些操作。以上介紹的編譯過程的預處理階段就是處理這些預處理指令的。預處理指令相當多,大多放在源程序文件的開頭部分。

注意:在一些系統(tǒng)中,頭文件名是不區(qū)分大小寫的,但在 #include 指令中,這些文件名通常是小寫的。

4.3、main() 函數(shù)

int main()
{
    printf("Hello world!\n"); 
    return 0;
}

每個 C 程序必須有一個 main() 函數(shù) —— 每個程序都是由這個函數(shù)開始執(zhí)行的。

五、預處理器

以上實例中展示了如何使用預處理指令 —— 把頭文件的內(nèi)容添加到源文件中。編譯的預處理階段做的工作遠不止這些。除了指令之外,源文件還可以包含宏。宏是提供給預處理器的指令,來添加或是修改程序中的語句。

宏可以很簡單,例如只定義一個符號:INDEX_FOOT,只要出現(xiàn)這個符號就使用 10 代替,如下所示:

# define INDEX_FOOT 10

宏也可以很復雜,根據(jù)特定條件可以將大量代碼添加到源文件中,例如在 Android 的 Binder 中,c 層代碼常使用宏添加代碼到源文件中,代碼如下所示:

#define DECLARE_META_INTERFACE(FregService)
    static const android::String16 descriptor;
    static android::sp<I##INTERFACE> asInterface(const android::sp<android::IBinder>& obj);
    virtual const android::String16& getInterfaceDescriptor() const;
    I##INTERFACE();
    virtual ~I##INTERFACE();

在 IFregService.c 中展開后:

static const android::String16 descriptor; // 描述接口名稱
static android::sp<IFregService> asInterface(const android::sp<android::IBinder>& obj); // 將IBinder對象轉(zhuǎn)化為IFregService接口
virtual const android::String16& getInterfaceDescriptor() const;
IFregService(); // 構造函數(shù)
virtual ~IFregService(); // 析構函數(shù)

參考

Beginning C
深入理解計算機操作系統(tǒng)

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • TITLE: 編程語言亂燉 碼農(nóng)最大的煩惱——編程語言太多。不是我不學習,這世界變化快! 有時候還是蠻懷念十幾、二...
    碼園老農(nóng)閱讀 5,594評論 2 35
  • 妮妮/當我們不與生活茍且,會發(fā)生什么? 最近英國小伙Jack Morris火爆網(wǎng)絡,當然不是因為他涉及到了韓國總統(tǒng)...
    龍妮妮閱讀 1,566評論 0 4
  • 晚上習慣性刷微信文章,看到這樣一段話“情商決定你能不能意識到別人不開心,而人品考驗的是你想不想讓別人不開心。大部分...
    言小胡閱讀 196評論 0 0
  • 十四年,似乎足夠沖淡一切,在文華東方酒店門前依舊一片花海,不知道人們還能堅持多少年,也許只要張國榮的歌曲和電影依舊...
    神槍工作室閱讀 379評論 0 1
  • O 優(yōu)美的環(huán)境、清新的空氣、潔凈的水源是人們實現(xiàn)美好生活的必備條件,特別是在經(jīng)歷過霧霾,在空氣遭到污染的環(huán)境里生活...
    果果親閱讀 93評論 0 1

友情鏈接更多精彩內(nèi)容