欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

printf格式化输出%x时的分析

程序员文章站 2022-03-08 15:01:09
...

使用printf(“%x”,…);可以输出指定参数的16进制形式,但是在实际的使用中,参数不一定都是32位的整数,有可能是16位的short,8位的char。如果使用printf %x 输出short和char会是什么结果呢? 
为此,在VS2015编写简单代码如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int l;
    short s;
    char c;
    l = 0xdeadbeef;
    s = l;
    c = l;
    printf("%x\n", l);
    printf("%x\n", s);
    printf("%x\n", c);
    system("pause");
    return 0;
}

分析
代码比较简单,首先定义了3个变量,然后对三个变量进行赋值,在赋值的过程中会发生宽度溢出。
s=l 将32位的整数放入16位的short,那么s的值只是l的低16位,即s=0xbeef;
c=l 将32位的整数放入8位的char,那么c的值应该只是l的低8位,即s=0xef;
因此预测输出:
deadbeef
beef
ef

我们运行一下代码看分析是否正确: 
运行结果: 
printf格式化输出%x时的分析 
与我们分析的还是有些差别,差别在于第二个与第三个输出多出了好多ff. 
那么这些ff是怎么来的呢? 
我们在printf(“%x”,s);加断点看反汇编的结果:

0017140E  movsx       eax,word ptr [s]  
00171412  mov         esi,esp  
00171414  push        eax  
00171415  push        175858h  
0017141A  call        dword ptr ds:[179118h] 

movsx eax,word ptr [s];后,我们看看eax的值: 
EAX = FFFFBEEF 
看来参数压栈之前,参数的值就已经是FFFFBEEF了,也难怪会打印出FFFFBEEF,而不是打印BEEF. 
那么为什么经过 movsx eax,word ptr [s]后 eax的高16位会变化为ffff? 
这需要从movsx说起了。 
MOVSX 指令:带符号扩展传送指令,将第二个操作数当作有符号类型取其符号位进行扩展。 
movsx eax,word ptr [s] 
word ptr [s]的值为0xbeef,二进制为1011 1110 1110 1111 
最高位(符号位)为1,于是扩展的时候会将eax的高16位全部扩展为1.于是就输出了如上结果。 
那么如果s的最高位为0呢?会如何扩展呢?那高位就应该扩展0.我们对代码稍加修加。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int l;
    short s;
    char c;
    l = 0xdead0eef;
    s = l;
    c = l;
    printf("%x\n", l);
    printf("%x\n", s);
    printf("%x\n", c);
    system("pause");
    return 0;
}

我们将l的deadbeef改成dead0eef,为的是让s的最高位为0。 
看输出结果: 
printf格式化输出%x时的分析 
可以看到因为s的最高位为0,于是输出的时候没有 了ffff出现。 
但是c的最高位为1(因为c==1110 1111),eax的高位全部扩展为1了,于是第三个输出还是扩展的FFFFFFEF。

相关标签: printf