从printf说开去(四)

   (接上文)

 

   对于函数:

   float sumfunf(int num, …)
   {
         char* args = (char*)(&num)+sizeof(num);

         double result = 0;
         for(int n = 0; n < num; n++)
         {
              result += *(double*)(args + n*sizeof(double));
         }

         return (float)result;
   }
  
   第一个固定参数传入2,代表累加的数字个数为2,不定参数部分传入1和2作为参数,调用函数看看结果:
   float r = sumfunf(2, 1, 2),此时的r值也是不确定的。(如果用vc7以上做一个最简的控制台测试,大部分情况下r的值返回应该是0。)

   sumfunf函数在处理时,首先取得不定参数部分的偏移量,接下来按照“传入的参数都是double”这个假定进行处理。而sizeof(double)为8,也即是说,
   函数期望以8个字节为单位读取堆栈数据,此时的堆栈情况如下:

 

                                                                       栈顶
                                          ———————————————————————————————
                                             第一个参数num(用于表示不定参数个数),占用空间4字节。值为0×00000002
不定参数起始地址–>  ———————————————————————————————-
                                                0×00000001 (4字节)
                                                ————————–
                                                0×00000002 (4字节)
                                                ————————–
                                                …其他已经入栈的数据

 

        通过看上面这个堆栈示意,问题应该已经很明显了,sumfunf函数在取参数的时候是期望以double为单位获取数据的,机器并不知道这个数据是什么“类型”,
       机器按double(8个字节)占用的字节空间读取,这样,传入的1、2这两个值被当成了一个数据读出来,按照double的规则进行处理,读出的值为:0×0000000200000001 (此例中使用Big endian序)。

 

       但是,问题出现了,这才读出一个参数,我们还有一个参数到哪里去读呢?

       sumfunf函数在读出第一个参数后,将其值累加至result变量中,在读取第二个参数时,取值的地址范围已经超出了我们传入的数据,然而程序并不会就此出错,恰好在这几个参数之后的堆栈地址中,数据是有效的!

 

       这些数据可被正确的访问,因此,函数在处理第二个参数会继续顺序读取8个字节!这8个字节的值作为第二个参数进行处理,回顾一下双精度浮点数的计算方式,对于0×0000000200000001这样一个数来说,是近乎于0的,因此,最后返回的值是不是为0,就取决于函数从堆栈中顺序读取出来的第二个参数,而这个值是不确定的!我们之所以看到返回值为0.000000,是因为对于这个简单程序而言,在特定的环境下,大部分情况下,读取出来的第二个不定参数在转换为double后,也是接近于0的。

 

   回到printf(“%f”,10/3);的问题来说,产生大家看到的不确定结果也是这个原理。

   对于printf(“%f”, 10/3, 0×40080000);这个能获得3.000000的结果,不妨尝试先自行分析一下。

 

(未完待续)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值