源码1
#include <stdio.h>
void f1() {
double x = -5.5625;
printf("%d\n",x); //输出为0,为什么?
}
int main() {
f1();
return ;
}
源码2
#include <stdio.h>
void f1() {
int y=;
printf("%f\n",y); //输出的值是随机的, 为什么?
}
int main() {
f1();
return ;
}
以下是利用gdb跟踪调试源码1的过程, 发现, printf("%d\n",x);根本就没有把x由double类型转换为int类型, 只是截取了x的低4个字节, 并输出
(gdb) b main
(gdb) r
Breakpoint , main () at .c:
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()
(gdb) p/x (char[])*0x8048480
$ = {0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x16, 0xc0} ;证明了内存0x8048480处存储常量-5.5625
(gdb) si
double x = -5.5625;
fstpl 0xfffffff8(�p) ;把浮点寄存器%st()的值置入内存(�p-)处
(gdb) info all-registers
st0 -5.5625 (raw 0xc001b200000000000000) ;证明了%st()存储的浮点数为-5.5625
(gdb) si
printf("%x\n",x);
fldl 0xfffffff8(�p) ;把内存(�p-)处的双精度浮点数置入%st(),即-5.526
(gdb)
printf("%x\n",x);
fstpl 0x4(%esp) ;把%st()中的值置入内存(%esp+),即把printf的第二参数压栈
(gdb) i r esp
esp 0xbfb00320 0xbfb00320
(gdb) p/x (char[])*0xbfb00324
$ = {0x6c, 0x95, 0x4, 0x8, 0x38, 0x3, 0xb0, 0xbf}
(gdb) si
printf("%x\n",x);
movl $0x8048478,(%esp) ;把函数printf的第一个参数压入栈中,用栈来传递参数
(gdb) p/x (char[])*0xbfb00324
;显示printf的第二个参数的值。printf的格式串中”%d”在指明第二参数是int类型,即使实际传递的;是double类型,也没有进行类型转换,即没有把x由double类型转换为int类型,printf在取值是
;直接读取前4个字节00 ,所以printf输出为0
$ = {0x0, 0x0, 0x0, 0x0, 0x0, 0x40, 0x16, 0xc0} ;
(gdb) si
printf("%x\n",x);
call 0x8048298 ;调用printf函数
(gdb) p/x (char[])*0xbfb00324
$ = {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 = ;
printf("%f\n",x); ;输出的值是随机的
}
对应的汇编代码:
movl $, -(�p)
movl -(�p), �x
movl �x, (%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\n",x);
}
对应汇编代码:
movb $, -(�p)
movsbl -(�p),�x ;把x符号扩展为4个字节
movl �x, (%esp)
movl $.LC0, (%esp)
call printf