The Compiler (关于编译的点点滴滴)

在objc上看到的  记录下笔记 

http://objccn.io/issue-6-2/

编译器做些什么?

本文主要探讨一下编译器主要做些什么,以及如何有效的利用编译器。

简单的说,编译器有两个职责:把 Objective-C 代码转化成低级代码,以及对代码做分析,确保代码中没有任何明显的错误。

现在,Xcode 的默认编译器是 clang。本文中我们提到的编译器都表示 clang。clang 的功能是首先对 Objective-C 代码做分析检查,然后将其转换为低级的类汇编代码:LLVM Intermediate Representation(LLVM 中间表达码)。接着 LLVM 会执行相关指令将 LLVM IR 编译成目标平台上的本地字节码,这个过程的完成方式可以是即时编译 (Just-in-time),或在编译的时候完成。

LLVM 指令的一个好处就是可以在支持 LLVM 的任意平台上生成和运行 LLVM 指令。例如,你写的一个 iOS app, 它可以自动的运行在两个完全不同的架构(Inter 和 ARM)上,LLVM 会根据不同的平台将 IR 码转换为对应的本地字节码。

LLVM 的优点主要得益于它的三层式架构 -- 第一层支持多种语言作为输入(例如 C, ObjectiveC, C++ 和 Haskell),第二层是一个共享式的优化器(对 LLVM IR 做优化处理),第三层是许多不同的目标平台(例如 Intel, ARM 和 PowerPC)。在这三层式的架构中,如果你想要添加一门语言到 LLVM 中,那么可以把重要精力集中到第一层上,如果想要增加另外一个目标平台,那么你没必要过多的考虑输入语言。在书 The Architecture of Open Source Applications 中 LLVM 的创建者 (Chris Lattner) 写了一章很棒的内容:关于 LLVM 架构

在编译一个源文件时,编译器的处理过程分为几个阶段。要想查看编译 hello.m 源文件需要几个不同的阶段,我们可以让通过 clang 命令观察:

% clang -ccc-print-phases hello.m

0: input, "hello.m", objective-c
1: preprocessor, {
       
       0}, objective-c-cpp-output
2: compiler, {
       
       1}, assembler
3: assembler, {
       
       2}, object
4: linker, {
       
       3}, image
5: bind-arch, "x86_64", {
       
       4}, image

本文我们将重点关注第一阶段和第二阶段。在文章 Mach-O Executables 中,Daniel 会对第三阶段和第四阶段进行阐述。

预处理

每当编源译文件的时候,编译器首先做的是一些预处理工作。比如预处理器会处理源文件中的宏定义,将代码中的宏用其对应定义的具体内容进行替换。

例如,如果在源文件中出现下述代码:

#import <Foundation/Foundation.h>

预处理器对这行代码的处理是用 Foundation.h 文件中的内容去替换这行代码,如果 Foundation.h 中也使用了类似的宏引入,则会按照同样的处理方式用各个宏对应的真正代码进行逐级替代。

这也就是为什么人们主张头文件最好尽量少的去引入其他的类或库,因为引入的东西越多,编译器需要做的处理就越多。例如,在头文件中用:

@class MyClass;

代替:

#import "MyClass.h"

这么写是告诉编译器 MyClass 是一个类,并且在 .m 实现文件中可以通过 import MyClass.h 的方式来使用它。

假设我们写了一个简单的 C 程序 hello.c:

#include <stdio.h>

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

然后给上面的代码执行以下预处理命令,看看是什么效果:

clang -E hello.c | less

接下来看看处理后的代码,一共是 401 行。如果将如下一行代码添加到上面代码的顶部::

#import <Foundation/Foundation.h>

再执行一下上面的预处理命令,处理后的文件代码行数暴增至 89,839 行。这个数字比某些操作系统的总代码行数还要多。

幸好,目前的情况已经改善许多了:引入了模块 - modules功能,这使预处理变得更加的高级。

自定义宏

我们来看看另外一种情形定义或者使用自定义宏,比如定义了如下宏:

#define MY_CONSTANT 4

那么,凡是在此行宏定义作用域内,输入了 MY_CONSTANT,在预处理过程中 MY_CONSTANT 都会被替换成 4。我们定义的宏也是可以携带参数的, 比如:

#define MY_MACRO(x) x

鉴于本文的内容所限,就不对强大的预处理做更多、更全面的展开讨论了。但是还是要强调一点,建议大家不要在需要预处理的代码中加入内联代码逻辑。

例如,下面这段代码,这样用没什么问题:

#define MAX(a,b) a > b ? a : b

int main() {
  printf("largest: %d\n", MAX(10,100));
  return 0;
}

但是如果换成这么写:

#define MAX(a,b) a > b ? a : b

int main() {
  int i = 200;
  printf("largest: %d\n", MAX(i++,100));
  printf("i: %d\n", i);
  return 0;
}

用 clang max.c 编译一下,结果是:

largest: 201
i: 202
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值