欢迎来到天天文库
浏览记录
ID:62033235
大小:38.50 KB
页数:8页
时间:2021-04-15
《C语言printf()函数深入分析.doc》由会员上传分享,免费在线阅读,更多相关内容在教育资源-天天文库。
1、C语言printf()函数深入分析 说起编程语言,C语言大家再熟悉不过.说起最简单的代码,Helloworld更是众所周知。一条简单的printf语句便可以完成这个简单的功能,可是 printf背后到底做了什么事情呢?可能很多人不曾在意,也或许你比我还要好奇!那我们就聊聊printf背后的故事.一、printf的代码在哪里?显然,Helloworld的源代码需要经过编译器编译,操作系统的加载才能正确执行。而编译器包含预编译、编译、汇编和链接四个步骤。#include<stdio.h〉intmain(){ printf(
2、”Hello World!\n”); return0;}首先,预编译器处理源代码中的宏,比如#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"..。externint printf(constchar*__restrict__format,.。.);。。.intmain(){ printf("HelloWorld!n")
3、; return0;}然后编译器将高级语言程序转化为汇编代码。$/usr/lib/gcc/i686-linux-gnu/4.7/cc1-fpreprocessed —quiet main.i -omain.s .file "main。c" 。section .rodata.LC0: .string ”Hello World!" .text 。globl main 。type main,@functionmain: pushl %ebp movl
4、 %esp, %ebp andl $—16, %esp subl $16, %esp movl $.LC0,(%esp) call puts movl $0, %eax leave ret 。size main,.-main。..我们发现printf函数调用被转化为callputs指令,而不是callprintf指令,这好像有点出乎意料。不过不用担心,这是编译器对printf的一种优化。实践证明,对于prin
5、tf的参数如果是以''结束的纯字符串,printf会被优化为puts函数,而字符串的结尾'\n’符号被消除。除此之外,都会正常生成callprintf指令。如果我们仍希望通过printf调用”HelloWorld !”的话,只需要按照如下方式修改即可.不过这样做就不能在printf调用结束后立即看到打印字符串了,因为puts函数可以立即刷新输出缓冲区。我们仍然使用puts作为例子继续阐述。 .section .rodata。LC0: .string ”helloworld!" ...
6、call printf..。接下来,汇编器开始工作。将汇编文件转化为我们不能直接阅读的二进制格式--可重定位目标文件,这里我们需要gcc工具包的objdump命令查看它的二进制信息.可是我们发现call puts指令里保存了无效的符号地址。$as—omain.omain。s$objdump–dmain.omain。o: 文件格式 elf32—i386Disassemblyof section.text:00000000<main>: 0: 55 push %ebp
7、 1: 89e5 mov %esp,%ebp 3: 83e4 f0 and $0xfffffff0,%esp 6: 83ec 10 sub $0x10,%esp 9: c70424000000 00 movl $0x0,(%esp) 10: e8 fcffffff call 11〈main+0x11> 15: b8000000 00 mov $0x0,%eax 1a: c9
8、 leave 1b: c3 ret而链接器最终会将puts的符号地址修正.由于链接方式分为静态链接和动态链接两种,虽然链接方式不同,但是不影响最终代码对库函数的调用。我们这里关注printf函数背后的原理,因此使用更易说明问题的
此文档下载收益归作者所有