当我们在Xcode中构建一个程序的时候,其中有一部分就是把源文件(.m和.h)文件转变成可执行文件。这个可执行文件包含了将会在CPU(iOS设备上的arm处理器或者你mac上的Intel处理器)运行的字节码。
我们将会过一遍编译器这个过程的做了些什么,同时也看一下可执行文件的内部到底是怎样的。其实,里面的东西比你看到的要多很多。
让我们先把Xcode放一边,踏入Commond-Lines的大陆。当我们在Xcode中构建一个App时,Xcode只是简单的调用了一系列的工具而已。希望这将会让你更好的明白一个可执行文件(被称之为Mach-O可执行文件),是怎样组装起来的,并且是怎样在iOS或者os x上执行的
XCrun
先从一些基础性的东西开始:我们将会使用一个叫做Xcrun的命令行工具。他看起来很奇怪,但是的确相当出色。这个小工具是用来调用其他工具的。 原先的时候我们执行:
1 |
% clang -v |
现在在终端中,我们可以执行:
1 |
% xcrun clang -v |
Xcrun定位Clang,并且使用相关的参数来执行Clang。
为什么我们要做这个事情?这看起来毫无重点,胡扯八道。但是Xcrun允许我们使用多个版本的Xcode,或者使用特定Xcode版本里面的工具,或者针对特点的SDK使用不同的工具。如果你恰好有Xcode4.5和xcode5、使用xcode-select和xcrun你可以选择选择使用来自Xcode4.5里面的SDK的工具,或者来自Xcode5里面的SDK的工具。在大多数其他平台上,这将是一个不可能的事情。如果你看一下帮助手册上xcode-select和xcrun的一些细节。你就能在不安装命令行工具的情况下,使用在终端中使用开发者工具。
一个不使用IDE的Hello World
回到终端,创建一个包含一个c文件的目录:
1 2 3 4 5 |
% mkdir ~/Desktop/objcio-command-line % cd !$ % touch helloworld.c |
现在使用你喜欢的文本编辑器来编辑这个文件,例如TextEdit.app:
1 |
% open -e helloworld.c |
录入下面的代码:
1 2 3 4 5 6 |
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello World!\n"); return 0; } |
保存,并且回到终端执行:
1 2 3 |
% xcrun clang helloworld.c % ./a.out |
现在你能够在终端上看到熟悉的Hello World!。你编译了一个C程序并且执行了它。所有都是在不使用IDE的情况下做的。深呼吸一下,高兴高兴。
我们在这里做了些什么?我们将hellowrold.c编译成了叫a.out的Mach-o二进制文件。a.out是编译器的默认名字,除非你指定一个别的。
Hello World和编译器
现在可选择的编译器是Clang(读作:/’kl /)。Chris写了一些更多关于Clang细节的介绍,可以参考: about the compiler
概括一下就是,编译器将会读入处理hellowrold.c,输出可执行文件a.out。这个过程包含了非常多的步骤。我们所要做的就是正确的执行它们。
预处理:
- 序列化
- 宏定义展开
- #include展开(引用文件展开)
语法和语义分析:
- 使用预处理后的单词构建词法树
- 执行语义分析生成语法树
- 输出AST (Abstract Syntax Tree)
代码生成和优化
- 将AST转化成更低级的中间码(LLVM IR)
- 优化生成代码
- 目标代码生成
- 输出汇编代码
汇编程序
- 将汇编代码转化成目标文件
连接器
- 将多个目标文件合并成可执行文件(或者一个动态库) 我们来看一个关于这些步骤的简单的例子。
预处理
编译器将做的第一件事情是处理文件。使用Clang展示一下这个过程:
1 |
% xcrun clang -E helloworld.c |
欧耶。输出了413行内容。打开个编辑器看看到底发生了什么:
1 |
% xcrun clang -E helloworld.c | open -f |
在文件顶部我们能看到很多以”#”开头的行。这些被称之为行标记语句的语句告诉我们它后面的内容来自哪里。我们需要这个。如果我再看一下hellowrold.c,第一行是:
1 |
#include <stdio.h> |
我们都用过#include和#import。它们做的就是告诉于处理器在#include语句的地方插入stdio.h的内容。在刚刚的文件里就是插入了一个以#开头的行标记。跟在#后面的数字是在源文件中的行号。每一行最后的数字是在新文件中的行号。回到刚才打开的文件,接下来是系统头文件,或者一些被看成包裹着extern “C”的文件。
如果你滚动到文件末尾,你将会发现我们的helloworld.c的代码:
当我们在Xcode中构建一个程序的时候,其中有一部分就是把源文件(.m和.h)文件转变成可执行文件。这个可执行文件包含了将会在CPU(iOS设备上的arm处理器或者你mac上的Intel处理器)运行的字节码。
我们将会过一遍编译器这个过程的做了些什么,同时也看一下可执行文件的内部到底是怎样的。其实,里面的东西比你看到的要多很多。
让我们先把Xcode放一边,踏入Commond-Lines的大陆。当我们在Xcode中构建一个App时,Xcode只是简单的调用了一系列的工具而已。希望这将会让你更好的明白一个可执行文件(被称之为Mach-O可执行文件),是怎样组装起来的,并且是怎样在iOS或者os x上执行的
XCrun
先从一些基础性的东西开始:我们将会使用一个叫做Xcrun的命令行工具。他看起来很奇怪,但是的确相当出色。这个小工具是用来调用其他工具的。 原先的时候我们执行:
1 |
% clang -v |
现在在终端中,我们可以执行:
1 |
% xcrun clang -v |
Xcrun定位Clang,并且使用相关的参数来执行Clang。
为什么我们要做这个事情?这看起来毫无重点,胡扯八道。但是Xcrun允许我们使用多个版本的Xcode,或者使用特定Xcode版本里面的工具,或者针对特点的SDK使用不同的工具。如果你恰好有Xcode4.5和xcode5、使用xcode-select和xcrun你可以选择选择使用来自Xcode4.5里面的SDK的工具,或者来自Xcode5里面的SDK的工具。在大多数其他平台上,这将是一个不可能的事情。如果你看一下帮助手册上xcode-select和xcrun的一些细节。你就能在不安装命令行工具的情况下,使用在终端中使用开发者工具。
一个不使用IDE的Hello World
回到终端,创建一个包含一个c文件的目录:
1 2 3 4 5 |
% mkdir ~/Desktop/objcio-command-line % cd !$ % touch helloworld.c |
现在使用你喜欢的文本编辑器来编辑这个文件,例如TextEdit.app:
1 |
% open -e helloworld.c |
录入下面的代码:
1 2 3 4 5 6 |
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello World!\n"); return 0; } |
保存,并且回到终端执行:
1 2 3 |
% xcrun clang helloworld.c % ./a.out |
现在你能够在终端上看到熟悉的Hello World!。你编译了一个C程序并且执行了它。所有都是在不使用IDE的情况下做的。深呼吸一下,高兴高兴。
我们在这里做了些什么?我们将hellowrold.c编译成了叫a.out的Mach-o二进制文件。a.out是编译器的默认名字,除非你指定一个别的。
Hello World和编译器
现在可选择的编译器是Clang(读作:/’kl /)。Chris写了一些更多关于Clang细节的介绍,可以参考: about the compiler
概括一下就是,编译器将会读入处理hellowrold.c,输出可执行文件a.out。这个过程包含了非常多的步骤。我们所要做的就是正确的执行它们。
预处理:
- 序列化
- 宏定义展开
- #include展开(引用文件展开)
语法和语义分析:
- 使用预处理后的单词构建词法树
- 执行语义分析生成语法树
- 输出AST (Abstract Syntax Tree)
代码生成和优化
- 将AST转化成更低级的中间码(LLVM IR)
- 优化生成代码
- 目标代码生成
- 输出汇编代码
汇编程序
- 将汇编代码转化成目标文件
连接器
- 将多个目标文件合并成可执行文件(或者一个动态库) 我们来看一个关于这些步骤的简单的例子。
预处理
编译器将做的第一件事情是处理文件。使用Clang展示一下这个过程:
1 |
% xcrun clang -E helloworld.c |
欧耶。输出了413行内容。打开个编辑器看看到底发生了什么:
1 |
% xcrun clang -E helloworld.c | open -f |
在文件顶部我们能看到很多以”#”开头的行。这些被称之为行标记语句的语句告诉我们它后面的内容来自哪里。我们需要这个。如果我再看一下hellowrold.c,第一行是:
1 |
#include <stdio.h> |
我们都用过#include和#import。它们做的就是告诉于处理器在#include语句的地方插入stdio.h的内容。在刚刚的文件里就是插入了一个以#开头的行标记。跟在#后面的数字是在源文件中的行号。每一行最后的数字是在新文件中的行号。回到刚才打开的文件,接下来是系统头文件,或者一些被看成包裹着extern “C”的文件。
如果你滚动到文件末尾,你将会发现我们的helloworld.c的代码:
1 2 |