c语言 对printf函数的理解

来源:互联网 发布:手机淘宝天天特价女装 编辑:程序博客网 时间:2024/05/16 14:37

今天无意间看了个小资料,是关于表达式和赋值运算符的。它是这么说的:一个基本整形变量 int   r  ;如果给他赋值r =  60000;60000明显超出了int 类型数据的范围(-32768--32767),但是因为它是赋值运算符,所以结果会以右侧的表达式的类型为准,然后r 就会自动转换成long   int   ,所以 r 的值就是60000。如果r = 30000 + 30000,因为30000在int型数据的范围内,所以按int类型数据处理,所以r 的类型会跟表达式的操作数保持一致,也是int 型,但结果很明显30000+30000 = 60000 超出了int 型数据的范围,所以结果不会是60000。对此我有些疑问,所以就自己写程序试试。程序我是这么写的:

程序1 :

#include "stdio.h"

main()
{
     int r ;
     clrscr();
     r =60000;
     printf("%d       %ld\n",r , r);

     r = 30000+30000;
     printf("%d       %ld\n",r , r);
}

输出的结果是这样的:

-5536       77851232
-5536       77851232

分析:以%d格式输出,即将数据以16位进行处理的,有符号数的范围是-32768~32767,因为60000超出了范围,所以得到的结果是负的(因为超出了32767以后又会从-32768开始计数),这没问题。但是后面的结果却很让人费解,为什么是这样的?。。。

我又将程序改动了一下,把r 改成了long int 类型,即

程序2:

#include "stdio.h"

main()
{
     long  int r ;
     clrscr();
     r =60000;
     printf("%d       %ld\n",r , r);

     r = 30000+30000;
     printf("%d       %ld\n",r , r);
}

输出的结果是这样的:

-5536       -362807296
-5536       -362741761

为什么 r 改成了 长整型 long  int  时,输出的结果仍旧不正确呢?

想了想printf()函数,并且和汇编结合起来分析,我们以程序2为例,分析的结果是这样的:

主函数在调用printf()函数之前,要将printf()函数的参数入栈,而且是按照参数本身的数据类型倒序入栈。在该程序中,printf()函数有两个参数,均为r 。所以在主函数调用printf()函数之前要将r 入栈因为r 是long   int  类型的,所以占32位,用十六进制表示为0000 EA60。r 入栈两次,栈中的内容是这样的:

60 ---------------------->top

EA

00

00

60

EA

00

00    ------------------->   bottom   

参数的入栈编译成汇编指令是这样的:

-u cs:1fa
0B9B:01FA 55            PUSH    BP
0B9B:01FB 8BEC          MOV     BP,SP
0B9B:01FD 83EC04        SUB     SP,+04
0B9B:0200 E8AE12        CALL    14B1
0B9B:0203 C746FE0000    MOV     WORD PTR [BP-02],0000
0B9B:0208 C746FC60EA    MOV     WORD PTR [BP-04],EA60
0B9B:020D FF76FE        PUSH    [BP-02]         ----------->0000入栈
0B9B:0210 FF76FC        PUSH    [BP-04]         ------------>EA60入栈
0B9B:0213 FF76FE        PUSH    [BP-02]         ------------->0000再次入栈
0B9B:0216 FF76FC        PUSH    [BP-04]       --------------->EA60再次入栈
0B9B:0219 B89401        MOV     AX,0194    

0B9B:021C 50            PUSH    AX
0B9B:021D E8DB08        CALL    0AFB          ---------------->调用printf()函数

你可能要问了,你说这么多,又汇编,又入栈的,跟这个程序的结果有什么关系啊!别着急,听我慢慢道来微笑

参数已经入栈了,调用printf()函数以后,printf()函数还要调用其他的函数,去完成一些功能,可能要调用好几层,这和我们没关系,我们不去管它,就只看我们的参数。printf()函数中的格式控制符(如%d)是控制参数的输出格式的,即——如果我们写%d ,那么我们的数据将以int 类型输出(16位的),如果我们写%ld , 那么我们的数据就会以长整型 long   int  输出(32位的)。经过我的验证,printf()函数在遇到格式控制符的时候,是这样取栈中的数据的:

以程序2 为例,printf()先遇到了%d , 所以从栈中取出一个16位数据,按照我们上面栈中存储的内容,它会将EA60出栈,按照 int 类型进行输出EA60转换成2进制数是1110  1010  0110  0000最高位是符号位,符号位为1,所以是负数,转换成10进制数是 -1*2的15次方 + 1*2的14次方 + 1*2的13次方 + 1*2的11次方 + 1*2的9次方 + 1* 2的6次方 + 1* 2的5次方 =  - 5536;目前栈中的数据如下:

00  --------------------->top

00

60

EA

00

00    ------------------->   bottom   

接下来 printf()函数又遇到了 %ld  (即要求数据以长整型格式输出)。所以需要将栈中的32位数据出栈即 EA 60 00 00出栈并进行输出,最高位符号位仍然为1,所以结果是负数,按照同样的方法,我们可以得到与他相对应的10进制数为  -362807296。目前栈中的内容为:

00    -------------------> top

00    ------------------->   bottom   

如果我们在程序2的基础上,将printf语句改为printf(“%d               %ld              %d \n”,r  , r  );

那么最后一个%d 所对应输出的结果是0 ,就是将我们栈中的最后一个字(16位)的内容进行出栈并输出。不信你可以尝试一下。

这就是我的分析结果,如果不得当之处,还请高手多多指教!微笑

原创粉丝点击