- 1、本文档共8页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 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?????????????????? ? leave?
? 1b
您可能关注的文档
- CAD教案复制、偏移、镜像旋转移动命令.doc
- CAD文件中光栅图象的路径问题.doc
- CAD机械制图员高级考试1答案.doc
- CAD燕秀外挂使用问题解答.doc
- CAD期末复习资料620.doc
- CAD画图中的比例问题.docx
- CAD笔B试卷(含答案).doc
- CAD练习题(试题类型).doc
- CAD绘制定位块教案.doc
- CAD绘图中常用到的问题解决方案.doc
- 2018-2019学年北京市朝阳区七年级(下)期末历史试卷.pdf
- 2018-2019学年北京市朝阳区七年级(下)期末英语试卷.pdf
- 2018-2019学年北京市延庆区八年级(下)期末生物试卷.pdf
- 2018-2019学年北京市海淀区首师大附中高二(下)期末历史试卷.pdf
- 2018-2019学年北京市顺义区七年级(下)期末地理试卷.pdf
- 2018-2019学年北京市丰台区八年级(下)期末语文试卷.pdf
- 2018-2019学年北京市东城区八年级(下)期末生物试卷.pdf
- 2018-2019学年北京市西城区七年级(下)期末地理试卷.pdf
- 2018-2019学年北京市东城区八年级(下)期末语文试卷.pdf
- 2018-2019学年北京市西城区高一(下)期末英语试卷.pdf
文档评论(0)