c语言printf分析,c语言printf函数深入分析

41528d3028836879cd698677c3999917.gifc语言printf函数深入分析

C 语言 printf()函数深入分析说 起编程语言,C 语言大家再熟悉不过。说起最简单的代码,Helloworld 更是众所周知。一条简单的 printf 语句便可以完成这个简单的功能,可是 printf 背后到底做了什么事情呢?可能很多人不曾在意,也或许你比我还要好奇!那我们就聊聊 printf 背后的故事。一、printf 的代码在哪里?显然,Helloworld 的源代码需要经过编译器编译,操作系统的加载才能正确执行。而编译器包含预编译、编译、汇编和链接四个步骤。#includeint 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 __at, .);.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, @functionmain:pushl %ebpmovl %esp, %ebpandl $-16, %espsubl $16, %espmovl $.LC0, (%esp)call putsmovl $0, %eaxleaveret.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.omain.o: 文件格式 elf32-i386Disassembly of section .text:00000000 :0: 55 push %ebp1: 89 e5 mov %esp,%ebp3: 83 e4 f0 and $0 xfffffff0,%esp6: 83 ec 10 sub $0 x10,%esp9: c7 04 24 00 00 00 00 movl $0 x0,(%esp)10: e8 fc ff ff ff call 11 15: b8 00 00 00 00 mov $0 x0,%eax1a: c9 leave 1b: c3 ret而链接器最终会将 puts 的符号地址修正。由于链接方式分为静态链接和动态链接两种,虽然链接方式不同,但是不影响最终代码对库函数的调用。我们这里关注 printf 函数背后的原理,因此使用更易说明问题的静态链接的方式阐述。$/usr/lib/gcc/i686-linux-gnu/4.7/collect2 \-static -o main \/usr/lib/i386-linux-gnu/crt1.o \/usr/lib/i386-linux-gnu/crti.o \/usr/lib/gcc/i686-linux-gnu/4.7/crtbeginT.o \main.o \--start-group \/usr/lib/gcc/i686-linux-gnu/4.7/libgcc.a \/usr/lib/gcc/i686-linux-gnu/4.7/libgcc_eh.a \/usr/lib/i386-linux-gnu/libc.a \--end-group \/usr/lib/gcc/i686-linux-gnu/4.7/crtend.o \/usr/lib/i386-linux-gnu/crtn.o$objdump –sd mainDisassembly of section .text:.08048ea4 :8048ea4: 55 push %ebp8048ea5: 89 e5 mov %esp,%ebp8048ea7: 83 e4 f0 and $0 xfffffff0,%esp8048eaa: 83 ec 10 sub $0 x10,%

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值