• 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  

     

  • 相关阅读:
    几款网络测试工具总结
    Linux安装telnet
    Linux下iptables 禁止端口和开放端口
    mysql创建某个数据库中的某张表 只读用户
    查看nginx版本号的几种方法
    Ngxtop-Nginx日志实时分析利器
    Nginx监控运维
    oracle经典书籍推荐
    华为典型局域网组网案例介绍(1)
    技术说明 路由器是如何工作的呢? 一个简单的解释
  • 原文地址:https://www.cnblogs.com/JohnABC/p/4469074.html
Copyright © 2020-2023  润新知