C语言printf[]函数深入分析.docVIP

  1. 1、本文档共8页,可阅读全部内容。
  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语言printf()函数深入分析 ?说 起编程语言,C语言大家再熟悉不过。说起最简单的代码,Helloworld更是众所周知。一条简单的printf语句便可以完成这个简单的功能,可是 printf背后到底做了什么事情呢?可能很多人不曾在意,也或许你比我还要好奇!那我们就聊聊printf背后的故事。 一、printf的代码在哪里? 显然,Helloworld的源代码需要经过编译器编译,操作系统的加载才能正确执行。而编译器包含预编译、编译、汇编和链接四个步骤。 #includestdio.h int main() { ??? printf(Hello World !\n); ??? return 0; } 首先,预编译器处理源代码中的宏,比如#include。预编译结束后,我们发现printf函数的声明。 $/usr/lib/gcc/i686-linux-gnu/4.7/cc1 -E -quiet?main.c -o main.i # 1 main.c # 1 命令行 # 1 main.c ... extern int printf (const char *__restrict __format, ...); ... int main() { ?printf(Hello World!\n); ?return 0; } 然后编译器将高级语言程序转化为汇编代码。 $/usr/lib/gcc/i686-linux-gnu/4.7/cc1 -fpreprocessed -quiet?main.i -o main.s ??? .file????? main.c ??? .section?? .rodata .LC0: ??? .string??? Hello World! ??? .text ??? .globl???? main ??? .type????? main, @function main: ??? pushl????? %ebp ??? movl?????? %esp,? %ebp ??? andl?????? $-16,? %esp ??? subl?????? $16,?? %esp ??? movl?????? $.LC0, (%esp) ??? call?????? puts ??? movl?????? $0,??? %eax ??? leave ??? ret ??? .size????? main, .-main ... 我 们发现printf函数调用被转化为call puts指令,而不是call printf指令,这好像有点出乎意料。不过不用担心,这是编译器对printf的一种优化。实践证明,对于printf的参数如果是以\n结束的纯 字符串,printf会被优化为puts函数,而字符串的结尾\n符号被消除。除此之外,都会正常生成call printf指令。 如果我们仍希望通过printf调用Hello World !\n的话,只需要按照如下方式修改即可。不过这样做就不能在printf调用结束后立即看到打印字符串了,因为puts函数可以立即刷新输出缓冲区。我们仍然使用puts作为例子继续阐述。 ??? .section?? .rodata .LC0: ??? .string??? hello world!\n ??? ... ??? call?????? printf ... 接下来,汇编器开始工作。将汇编文件转化为我们不能直接阅读的二进制格式——可重定位目标文件,这里我们需要gcc工具包的objdump命令查看它的二进制信息。可是我们发现call puts指令里保存了无效的符号地址。 $as -o main.o main.s $objdump –d main.o main.o:???? 文件格式 elf32-i386 Disassembly of section .text:main: ?? 0:? 55?????????????????? ? push?? %ebp ?? 1:? 89 e5??????????????? ? mov??? %esp,%ebp ?? 3:? 83 e4 f0???????????? ? and??? $0xfffffff0,%esp ?? 6:? 83 ec 10???????????? ? sub??? $0x10,%esp ?? 9:? c7 04 24 00 00 00 00 ??? movl?? $0x0,(%esp) ? 10:? e8 fc ff ff ff?????? call?? 11 main+0x11 ? 15:? b8 00 00 00 00?????? mov??? $0x0,%eax ? 1a:? c9????????????

文档评论(0)

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

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

1亿VIP精品文档

相关文档