一个 C 程序的艺术之旅.docVIP

  1. 1、本文档共17页,可阅读全部内容。
  2. 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
  3. 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载
  4. 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
  5. 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
  6. 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们
  7. 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
  8. 8、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
一个 C 程序的艺术之旅.doc

编译:一个 C 程序的艺术之旅作者:?李博杰 正在学习 CS50(一门 MOOC)的?@张静宁(497023976)??写了一篇《【读图学 C 语言】编译时发生了什么》。她的文章更多讲的是 “what”,本文将尝试解释一些 “why”。 C 程序为什么要编译才能执行?一个 C 程序在变成可执行文件的过程中,为什么要经过预处理、编译、汇编、链接这四道工序?让我们从这段简单的 C 程序开始。 为什么要编译 这并不是一个简单的问题。我们知道 Python 代码不需要 “编译”,输入一行代码就可以运行出结果了,对用户很友好有木有!这种交互式的运行环境被称为 REPL(Read-Evaluate-Print-Loop),也就是读取用户输入的语句,执行语句,输出语句的值,再返回到等待输入状态。 C 语言为什么不能用这种交互式的 REPL 呢?非也。在某个私有 SDK 里,为了便于调试,内嵌了一个 2.5 万行的 C 语言解释器,支持几乎所有的 C 语言语法。它使用 lex 和 yacc 做词法和语法分析,语法结构的名字都是从?C99 标准里抄的。在调试一个 API 的时候,只需在解释器里输入语句调用 API,而不需要写一个 C 文件、编译运行。 由于 C 语言的语句是没有值的,只有表达式有值,而该解释器只接受语句作为输入,因此 REPL 中的 print 步骤被省略了,只有在显式调用输出语句时才会有输出。这里与标准 C 语言的唯一区别是,语句不需要写在 main() 函数里,而是写一行执行一行。事实上,C 语言的 main() 函数是一个语法糖(syntactic sugar),也就是规定所有语句必须写在函数里。像 Pascal 这样的语言,就是把所有不在任何函数里的语句拿出来,组织成一个 “主函数”。 可见,阻止 C 语言解释执行的并不是其语法。那么为什么 C 开发环境一般不提供对用户友好的解释执行方式,而是要写一个格式严谨的 C 文件、编译执行呢?这是为了对机器友好,也就是提高程序运行的效率。时至今日,再用 C 语言写应用程序恐怕是要被笑话的(所以瀚海星云 BBS 总有人提出要重写),C 语言主要用于系统底层、嵌入式和追求高性能的场合。这些地方,程序员的时间比机器的时间便宜。 编译比解释执行的性能更高,用一个小例子就能看出。比如 c = a + b 这样一条语句,会被编译为如下的汇编码: 只有 4 行,很简洁吧!其中的 movl 是把数据从内存移动到寄存器或者从寄存器移动到内存,而实际的加法操作是在寄存器里完成的。 同样是 c = a + b,解释执行的话,解释器需要做这些事: 词法分析:读入 “c = a + b” 这个字符串,把它拆成 “c” “=” “a” “+” “b” 这些词素(lexeme); 语法分析:认出?“c” “=” “a” “+” “b” 这四个词素组成的是一个赋值表达式,而赋值表达式的右端是一个算术表达式,执行的是加法操作。形成一棵语法树; 生成中间代码:从符号表里找到 c,a,b 三个变量定义的位置,变成类似汇编码的形式(IL,Intermediate Language),例如 LLVM 中的 %c = add i32 %a %b; 执行中间代码:将中间代码载入内存,逐条执行。执行到这一条时,首先看当前要执行的中间代码是 add,跳转到 add 的处理过程,然后再真正执行 add 操作。 上述前三步是 C 这样的编译型语言在编译时也要做的,在编译时做就省下了运行的时间;而第 4 步中,编译生成的机器码不像执行中间代码那样需要查找 add 的中间步骤。 编译时间与运行时间是一种 trade-off,C 程序通常一次编译,多次运行或长时间运行,因此在编译上多耗些时间、多做些优化被认为是值得的。而解释型语言往往作为胶水语言,也就是完成一项用后即弃的特定任务。在 PHP 内核开发邮件列表里,一个月经贴是为什么 PHP 不做编译优化。官方的答复是,PHP 程序运行时间往往很短暂,比如 10 ms;如果花 100 ms 做编译优化,把运行时间压缩到 1 ms,总的时间消耗是 101 ms,反而更慢了(不考虑中间代码缓存)。 C 语言编译器经过数十年的发展,编译优化已经做到与手写汇编代码差不多的性能了,也就是用 C 写出的算法合理、实现高效的代码基本上能够发挥出机器的最大潜能,所以 C 语言成为各种 benchmark 的参考标准。下表中,Python、R、Matlab、Octave 等逐行解释执行的语言比 C 慢几十倍甚至更多。Fortran、Go 作为编译执行的语言也很快。JavaScript、Java 由于用了 JIT(Just-In-Time)技术,也是很快的,但与 C 还是有一定差距。 各种语言与 C 的性能对

您可能关注的文档

文档评论(0)

文档资料 + 关注
实名认证
文档贡献者

该用户很懒,什么也没介绍

1亿VIP精品文档

相关文档