关于C语言中printf函数“输出歧视”的问题

问题描述

昨天晚上被问到一个问题,为什么在同一个printf函数中两次输出一个double型变量会得不到正确的结果。具体代码大致如下:

#include <stdio.h>

int main()
{
int a;
double b;
double result; printf("Please input a: ");
scanf("%d", &a); printf("Please input b: ");
scanf("%lf", &b); printf("\n"); result = a * b;
printf("result_int: %d\nresult_double: %f\n", result, result); return 0;
}

看主函数的第18行语句,其中result作为一个double型变量,被以整型浮点型分别输出了一次,按照正常思路,输出应该是下面这样:

Please input a: 12
Please input b: 12.3 result_int: 858993460
result_double: 147.600000

即前一个按整型输出时,会按照double型低32位的数据,输出一个不期望的整数。

但是第二次输出时,则会按照其正确的形式(64位8字节double类型)输出一个预期中的浮点数:147.600000

结果却大出意料,实际输出是下面这样:

Please input a: 12
Please input b: 12.3 result_int: 858993460
result_double: 0.000000

探索问题原因

这样的结果让人很意外,第一时间就想到了是否是因为result本身出了问题,所以在原程序后加入了一句printf("\n%f\n", result);来确认result是否发生了意料之外的变化。

但是很显然,并不是result出了问题,因为单独输出的result依然是正常的147.600000

那么接下来,既然result本身没有问题,就应当想到是否在输出上出了问题。比如很显然地,%d格式是标准的Int型输出格式,正常来讲应该是32位数据,而double则是64位数据,是否是这里出了问题呢?

比如在同一句输出里面,对同一个变量多次不完整输出,导致了其地址的递进式输出。

当然,到此时,这只是我的一种猜测。

要验证这个想法,最简单的莫过于直接查看内存内容。

如图

转换为4字节整数显示结果为

与输出

result_lower_32 = 858993459
result_higher_32 = 1080193843
resule_now = 147.600000

相符。

到此,产生这一问题的原因已经弄清楚了,并且最终问题应该是出在printf函数的定义中,看来还是C语言基础不牢,今天开始有必要重新学习,认真研究其每个细节。

另一种研究方法

实际上一开始因为不太熟悉VS的调试操作,并没能很快地在调试界面找到方法查看具体变量的内存地址及其内容,原因一则是很久没有用C语言了,二则当初学的时候也没有认真,所以验证这个问题的过程十分艰辛,

中间还自己写了个函数想要输出数据的二进制格式,但是这个函数针对整型时工作正常,换成double型之后,由于没想到办法对double型和unsigned long long型直接进行位运算,所以迫不得已对double型数据进行了强制类型转换,最终发现强制类型转换会导致数据变化,无法保持其原型,所以这一思路作废。就此略过不表。

后来想起来C语言中可以对数据直接按16进制格式输出,故设计了如下程序进行尝试。

#include <stdio.h>

int main()
{
//该系统中,double为64位8字节,int和long为32位4字节。
//且为小端系统,即低位数据存储在内存的低位地址中。
double b; printf("Please input the values of b:\n");
scanf("%lf", &b); printf("\n");
printf("These are two parts of b:\n");
printf("\tlower byte = \t%#x\n\thigher byte = \t%#x\n\n", b, b); printf("This is the whole b:\n");
printf("\t%#llx\n", b); return 0;
}

输出如下(输入为12.3)

Please input the values of b:
12.3 These are two parts of b:
lower byte = 0x9999999a
higher byte = 0x40289999 This is the whole b:
0x402899999999999a

问题结论

从上述两种研究方法的结果,我们可以明显看到,在同一个输出格式字符串中,倘若针对某一个待输出量的转换说明不足以对应其所有的数据位,则printf函数仅仅按照转换说明对应的数据位数,从低到高,依次输出。

所以对于介于初学和中级之间的学习者而言,研究stdio.h头文件的源码函数很有必要的。

关于C语言中printf函数“输出歧视”的问题的更多相关文章

  1. c语言中printf()函数中的参数计算顺序

    今天看到了一个关于printf()函数计算顺序的问题,首先看一个例子: #include<stdio.h> int main() { printf("%d---%d---%d&q ...

  2. c语言中的c语言中realloc()函数解析

    c语言中realloc()函数解析 真是有点惭愧,这些内容本应该很早就掌握的,以前只是糊里糊涂的用,不知道在内存中具体是怎么回事,现在才弄清楚. realloc(void *__ptr, size_t ...

  3. C语言中system()函数的用法总结(转)

    system()函数功能强大,很多人用却对它的原理知之甚少先看linux版system函数的源码: #include <sys/types.h> #include <sys/wait ...

  4. 使用C语言中qsort()函数对浮点型数组无法成功排序的问题

    一 写在开头 1.1 本节内容 本节主要内容是有关C语言中qsort()函数的探讨. 二 问题和相应解决方法 qsort()是C标准库中的一个通用的排序函数.它既能对整型数据进行排序也能对浮点型数据进 ...

  5. R语言中apply函数

    前言 刚开始接触R语言时,会听到各种的R语言使用技巧,其中最重要的一条就是不要用循环,效率特别低,要用向量计算代替循环计算. 那么,这是为什么呢?原因在于R的循环操作for和while,都是基于R语言 ...

  6. (转)C语言中Exit函数的使用

    C语言中Exit函数的使用 exit() 结束当前进程/当前程序/,在整个程序中,只要调用 exit ,就结束return() 是当前函数返回,当然如果是在主函数main, 自然也就结束当前进程了,如 ...

  7. C语言中qsort函数用法

    C语言中qsort函数用法-示例分析    本文实例汇总介绍了C语言中qsort函数用法,包括针对各种数据类型参数的排序,非常具有实用价值非常具有实用价值. 分享给大家供大家参考.C语言中的qsort ...

  8. C笔记01:关于printf函数输出先后顺序的讲解

    关于printf函数输出先后顺序的讲解!! 对于printf函数printf("%d%d\n", a, b);函数的实际输出顺序是这样的先计算出b,然后再计算a,接着输出a,最后再 ...

  9. C语言中malloc函数返回值是否需要类型强制转换问题

    1. 在C语言中, 如果调用的函数没有函数原型, 则其返回值将默认为 int 型. 考虑调用malloc函数时忘记了 #include <stdlib.h>的情况 此时malloc函数返回 ...

随机推荐

  1. MFC 控件使用汇总

    一.动态创建button CButton *button=new CButton; button->Create(_T(,,,),);//最后一个是ID BEGIN_MESSAGE_MAP(CM ...

  2. Python——通过斐波那契数列来理解生成器

    一.生成器(generator) 先来看看一个简单的菲波那切数列,出第一个和第二个外,任意一个数都是由前两个数相加得到的.如:0,1,1,2,3,5,8,13...... 输入斐波那契数列前N个数: ...

  3. Ubantu下安装adobe flash player插件

    用火狐看视频,要打开Adobe官网下载xxxx,太麻烦. 可以在Terminal下输入: apt-get install flashplugin-nonfree 好了.

  4. nat123安装启动教程帮助

    转自:http://www.nat123.com/Pages_17_291.jsp 本文就nat123安装启动可能遇到的问题及与安全狗影响处理. 下载安装nat123客户端安装包.第一次安装使用,可选 ...

  5. hadoop文件写入

    转:http://blog.csdn.net/xiaoshunzi111/article/details/48198105 由上图可知;写入文件分为三个角色,分别是clientnode  nameno ...

  6. .net 实现远程控制 远程协助

    一个同事 用c#实现的远程协助功能 服务端 负责客户端连接 转发 客户端 1.开启服务端 2.客户端 3.输入对方用户名 点击控制

  7. Sequence(尺取)

    A sequence of N positive integers (10 < N < 100 000), each of them less than or equal 10000, a ...

  8. FatMouse' Trade(Hdu 1009)

    Description FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the wareho ...

  9. Java多线程-线程的同步(同步代码块)

    对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果. 追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问 ...

  10. 自制模仿apache访问日志文件格式的php日志类

    <?php // 访问日志写入类 @author 王伟 2011.12.14class Log{        //项目跟路径    private $root_path;        //日 ...