ios .mm文件调用c语言函数报错,深入浅出 iOS 编译

前言

两年前曾经写过一篇关于编译的文章《iOS编译过程的原理和应用》,这篇文章介绍了iOS编译相关基础知识和简单应用,但也很有多问题都没有解释清楚:

Clang和LLVM究竟是什么

源文件到机器码的细节

Linker做了哪些工作

编译顺序如何确定

头文件是什么?XCode是如何找到头文件的?

Clang Module

签名是什么?为什么要签名

为了搞清楚这些问题,我们来挖掘下XCode编译iOS应用的细节。

编译器

把一种编程语言(原始语言)转换为另一种编程语言(目标语言)的程序叫做编译器。

大多数编译器由两部分组成:前端和后端。

前端负责词法分析,语法分析,生成中间代码;

后端以中间代码作为输入,进行行架构无关的代码优化,接着针对不同架构生成不同的机器码。

前后端依赖统一格式的中间代码(IR),使得前后端可以独立的变化。新增一门语言只需要修改前端,而新增一个CPU架构只需要修改后端即可。

Objective C/C/C++使用的编译器前端是clang,swift是swift,后端都是LLVM。

6e8bcd402f4388d35ba15be21aa92f17.png

LLVM

LLVM(Low Level Virtual Machine)是一个强大的编译器开发工具套件,听起来像是虚拟机,但实际上LLVM和传统意义的虚拟机关系不大,只不过项目最初的名字是LLVM罢了。

LLVM的核心库提供了现代化的source-target-independent优化器和支持诸多流行CPU架构的代码生成器,这些核心代码是围绕着LLVM IR(中间代码)建立的。

基于LLVM,又衍生出了一些强大的子项目,其中iOS开发者耳熟能详的是:Clang和LLDB。

clang

clang是C语言家族的编译器前端,诞生之初是为了替代GCC,提供更快的编译速度。一张图了解clang编译的大致流程:

56cfa3506e996736b6132fa61b8cb567.png

接下来,从代码层面看一下具体的转化过程,新建一个main.c:

#include

// 一点注释

#define DEBUG 1

int main() {

#ifdef DEBUG

printf("hello debug\n");

#else

printf("hello world\n");

#endif

return 0;

}

预处理(preprocessor)

预处理会替进行头文件引入,宏替换,注释处理,条件编译(#ifdef)等操作。

#include "stdio.h"就是告诉预处理器将这一行替换成头文件stdio.h中的内容,这个过程是递归的:因为stdio.h也有可能包含其头文件。

用clang查看预处理的结果:

xcrun clang -E main.c

预处理后的文件有400多行,在文件的末尾,可以找到main函数

int main() {

printf("hello debug\n");

return 0;

}

可以看到,在预处理的时候,注释被删除,条件编译被处理。

词法分析(lexical anaysis)

词法分析器读入源文件的字符流,将他们组织称有意义的词素(lexeme)序列,对于每个词素,此法分析器产生词法单元(token)作为输出。

$ xcrun clang -fmodules -fsyntax-only -Xclang -dump-tokens main.c

输出:

annot_module_include '#include

int 'int' [StartOfLine]Loc=<4:1>4:1>

identifier 'main' [LeadingSpace]Loc=<4:5>4:5>

....

Loc=<1:1>标示这个token位于源文件main.c的第1行,从第1个字符开始。保存token在源文件中的位置是方便后续clang分析的时候能够找到出错的原始位置。1:1>

语法分析(semantic analysis)

词法分析的Token流会被解析成一颗抽象语法树(abstract syntax tree - AST)。

$ xcrun clang -fsyntax-only -Xclang -ast-dump main.c | open -f

main函数AST的结构如下:

�[0;34m`-�[0m�[0;1;32mFunctionDecl�[0m�[0;33m 0x7fcc188dc700�[0m �[0;33mline:4:5�[0m�[0;1;36m main�[0m �[0;32m'int ()'�[0m

�[0;34m `-�[0m�[0;1;35mCompoundStmt�[0m�[0;33m 0x7fcc188dc918�[0m

�[0;34m |-�[0m�[0;1;35mCallExpr�[0m�[0;33m 0x7fcc188dc880�[0m �[0;32m'int'�[0m�[0;36m�[0m�[0;36m�[0m

�[0;34m | |-�[0m�[0;1;35mImplicitCastExpr�[0m�[0;33m 0x7fcc188dc868�[0m �[0;32m'int (*)(const char *, ...)'�[0m�[0;36m�[0m�[0;36m�[0m

�[0;34m | | `-�[0m�[0;1;35mDeclRefExpr�[0m�[0;33m 0x7fcc188dc7a0�[0m �[0;32m'int (const char *, ...)'�[0m�[0;36m�[0m�[0;36m�[0m �[0;1;32mFunction�[0m�[0;33m 0x7fcc188c5160�[0m�[0;1;36m 'printf'�[0m �[0;32m'int (const char *, ...)'�[0m

�[0;34m | `-�[0m�[0;1;35mImplicitCastExpr�[0m�[0;33m 0x7fcc188dc8c8�[0m �[0;32m'const char *'�[0m�[0;36m�[0m�[0;36m�[0m

�[0;34m | `-�[0m�[0;1;35mImplicitCastExpr�[0m�[0;33m 0x7fcc188dc8b0�[0m �[0;32m'char *'�[0m�[0;36m�[0m�[0;36m�[0m

�[0;34m | `-�[0m�[0;1;35mStringLiteral�[0m�[0;33m 0x7fcc188dc808�[0m �[0;32m'char [13]'�[0m�[0;36m lvalue�[0m�[0;36m�[

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值