美文网首页Android-NDK/JNI
Android NDK 1 C语言基础

Android NDK 1 C语言基础

作者: seraphzxz | 来源:发表于2018-07-02 16:18 被阅读18次

一、C 语言概述

C 语言是由一个国际标准定义的,目前最新版本由 ISO/IEC 9899:2011文档 定义。当前版本一般称为 C11,但是 C11 中一些语言元素是可选的,这就意味着遵循 C11 的编辑并没有实现该变准中的所有功能。

C11 标准是 ISO/IEC 9899:2011 - Information technology -- Programming languages -- C 的简称,曾用名为 C1X。

C11 标准是 C 语言标准的第三版,前一个标准版本是 C99 标准。2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC) 旗下的C语言标准委员会(ISO/IECJTC1/SC22/WG14)正式发布了 C11 标准。

1.1 标准库

C 标准库也在 C11 标准中指定,标准库定义了编写 C 程序时常用的常量、符号和函数,还提供了一些 C 语言的一些可选扩展。标准库在一系列标准文件:头文件(.h)中指定。

二、创建 C 程序

C 程序的创建由以下四个步骤:

  1. 编辑
  2. 编译
  3. 链接
  4. 执行

下面就来详细分析每个步骤:

2.1、编辑

编辑过程就是创建和修改 C 程序的源代码。

2.2、编译

编译器将源代码转换为机器语言,在编译过程中会找出并报告错误。该阶段的输入是在编辑期产生文件(源文件)。编译器的输出结果称为对象代码(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 这个参数,那么程序还会自动链接,生成的文件默认为 a.out。

编译部分分为三个阶段:

  1. 预处理阶段

该阶段会修改或添加代码,预处理器(cpp)会根据以字符 # 开头的命令,修改原始的 C 程序。比如在程序中第一行为 #include <stdio.h>,那么该命令告诉预处理器读取头文件 stdio.h 的内容,并把它直接插入程序文本中。结果就得到另一个 C 程序,通常以 .i 作为文件扩展名。

指令如下:

    gcc -E hello.c -o hello.i
  1. 编辑阶段

该阶段是生成对象代码的实际编译过程,编译器(ccl)将 .i 文件翻译成文本文件(文件扩展名为 .s),它包含一个汇编语言程序

在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。汇编语言是非常有用的,它为不同高级语言不同编译器提供了通用的语言。

如:C 编译器和 Fortran 编译器产生的输出文件用的都是一样的汇编语言。

指令如下:

    gcc -S hello.i -o hello.s

hello.s 文件内容如下:

          .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

2.3、链接

链接器(ld)将原代码文件中由编译器产生的各种对象模块组合起来,再从 C 语言提供的程序库中添加必要的代码模块,将他们组合成一个可执行文件。链接器也可以检测和报告错误。如果连接成功会产生一个可执行文件,在 Linux/Unix 环境下,该文件无扩展名。

指令如下:

gcc hello.c -o hello

以上几个步骤可以使用以下指生成可执行文件:

gcc -o hello hello.c

2.4、执行

经过以上几个步骤,hello.c 已经被编译系统翻译成成了可执行目标文件 hello,并被保存在磁盘上。在 Linux/Unix 系统下只要在终端输入可执行文件名就可以执行程序。

指令:

linux> ./hello
hello,world

以上步骤可以总结为下图:

源文件转化为目标文件.png

三、示例

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

/*
 * first C program
 */
#include <stdio.h>

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

3.1、注释

C语言的注释分为两种:

  1. 以“//”开头的;
  2. 在“/”与“/”之间的;

3.2、预处理指令

以下代码行:

#include <stdio.h>

严格来说他并不是可执行程序的一部分,但是它很重要,程序缺少它是不可以执行的。符号“#”表示这是一个预处理指令,告诉编译器在编译源代码之前,要先做些操作。以上介绍的编译过程的预处理阶段就是处理这些预处理指令的。预处理指令相当多,大多放在源程序文件的开头部分。

注意:在一些系统中,头文件名是不区分大小写的,但在 #include 指令中,这些文件名通常是小写的。

3.3、main() 函数

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

每个 C 程序必须有一个 main() 函数 —— 每个程序都是由这个函数开始执行的。

四、预处理器

以上实例中展示了如何使用预处理指令 —— 把头文件的内容添加到源文件中。编译的预处理阶段做的工作远不止这些。除了指令之外,源文件还可以包含宏。宏是提供给预处理器的指令,来添加或是修改程序中的语句。

宏可以很简单,例如只定义一个符号:INDEX_FOOT,只要出现这个符号就使用10代替,如下所示:

# define INDEX_FOOT 10

宏也可以很复杂,根据特定条件可以将大量代码添加到源文件中,例如在 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对象转化为IFregService接口
virtual const android::String16& getInterfaceDescriptor() const;
IFregService(); // 构造函数
virtual ~IFregService(); // 析构函数

参考

Beginning C
深入理解计算机操作系统

由于刚刚接触 C 语言,加之本人水平有限,不足之处还请批评指正。

相关文章

网友评论

    本文标题:Android NDK 1 C语言基础

    本文链接:https://www.haomeiwen.com/subject/fhsluftx.html