源码1
#include <stdio.h> void f1() { double x = -5.5625; printf("%d ",x); //输出为0,为什么? } int main() { f1(); return 0; }
源码2
#include <stdio.h> void f1() { int y=1; printf("%f ",y); //输出的值是随机的, 为什么? } int main() { f1(); return 0; }
以下是利用gdb跟踪调试源码1的过程, 发现, printf("%d ",x);根本就没有把x由double类型转换为int类型, 只是截取了x的低4个字节, 并输出
(gdb) b main (gdb) r Breakpoint 1, main () at 1.c:9 9 f1(); (gdb) display /i $pc call 0x8048354 (gdb) si push �p ;保存上层函数的栈的上下文 (gdb) si mov %esp,�p ;保存上层函数的栈的上下文 (gdb) si sub $0x28,%esp ;为函数f1分配的栈,大小为28字节 (gdb) si double x = -5.5625; fldl 0x8048480 ;把0x8048480存储的双精度浮点数置入浮点寄存器%st(0) (gdb) p/x (char[8])*0x8048480 $1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x16, 0xc0} ;证明了内存0x8048480处存储常量-5.5625 (gdb) si double x = -5.5625; fstpl 0xfffffff8(�p) ;把浮点寄存器%st(0)的值置入内存(�p-8)处 (gdb) info all-registers st0 -5.5625 (raw 0xc001b200000000000000) ;证明了%st(0)存储的浮点数为-5.5625 (gdb) si printf("%x ",x); fldl 0xfffffff8(�p) ;把内存(�p-8)处的双精度浮点数置入%st(0),即-5.526 (gdb) printf("%x ",x); fstpl 0x4(%esp) ;把%st(0)中的值置入内存(%esp+4),即把printf的第二参数压栈 (gdb) i r esp esp 0xbfb00320 0xbfb00320 (gdb) p/x (char[8])*0xbfb00324 $2 = {0x6c, 0x95, 0x4, 0x8, 0x38, 0x3, 0xb0, 0xbf} (gdb) si printf("%x ",x); movl $0x8048478,(%esp) ;把函数printf的第一个参数压入栈中,用栈来传递参数 (gdb) p/x (char[8])*0xbfb00324 ;显示printf的第二个参数的值。printf的格式串中”%d”在指明第二参数是int类型,即使实际传递的;是double类型,也没有进行类型转换,即没有把x由double类型转换为int类型,printf在取值是 ;直接读取前4个字节00 00 00 00,所以printf输出为0 $3 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x16, 0xc0} ; (gdb) si printf("%x ",x); call 0x8048298 ;调用printf函数 (gdb) p/x (char[8])*0xbfb00324 $4 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x16, 0xc0}
由此引申开来发现如下规律:
%d/%x/%u --> float/double: 利用%d/%x/%u输出float/double类型变量时, 会得到意想不到的结果, 因为不会进行类型转换, 而是把变量截断为4个字节并输出, 原因在前面已经给出
%f --> int 利用%f输出int变量, 输出的值是随机的
void f1() { int x = 1; printf("%f ",x); ;输出的值是随机的 }
对应的汇编代码:
movl $1, -4(�p) movl -4(�p), �x movl �x, 4(%esp) ;没有把x转换为float类型 movl $.LC0, (%esp) ;printf会读取内存4(%esp)除的8个字节,由于后4个字节的值是随机的,所以输出的值是随机的 call printf
%d/%x/%u --> char/short
利用%d/%x/%u输出char/short类型变量时, 会对char/short类型进行符号位扩展, 扩展为4个字节
void f1(){ char x = 0x80; printf("%x ",x); }
对应汇编代码:
movb $1, -1(�p) movsbl -1(�p),�x ;把x符号扩展为4个字节 movl �x, 4(%esp) movl $.LC0, (%esp) call printf