C-printf/sprintf/snprintf中的类型转换详解

源码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  

 

原文地址:https://www.cnblogs.com/JohnABC/p/4469074.html