关于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. 【bzoj3942】[Usaco2015 Feb]Censoring

    [题目大意] 有一个S串和一个T串,长度均小于1,000,000,设当前串为U串,然后从前往后枚举S串一个字符一个字符往U串里添加,若U串后缀为T,则去掉这个后缀继续流程. [样例输入] whatth ...

  2. 【codevs2495】水叮当的舞步

    题目描述 Description 水叮当得到了一块五颜六色的格子形地毯作为生日礼物,更加特别的是,地毯上格子的颜色还能随着踩踏而改变.为了讨好她的偶像虹猫,水叮当决定在地毯上跳一支轻盈的舞来卖萌~~~ ...

  3. rsa 数学推论

    RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.但是有不少新来的同事对它不太了解,恰好看到一本书中作者用实例对它进行了简化 而生动的描述,使得高深的数学理论能够被容易地理解.我们 ...

  4. Java-Http

    1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.IOException; 4 im ...

  5. 面试题:volatile关键字的作用、原理

    在只有双重检查锁,没有volatile的懒加载单例模式中,由于指令重排序的问题,我确实不会拿到两个不同的单例了,但我会拿到“半个”单例. 而发挥神奇作用的volatile,可以当之无愧的被称为Java ...

  6. codefirst 关系处理

    1.http://www.cnblogs.com/libingql/archive/2013/01/31/2888201.html 2.多对多 protected override void OnMo ...

  7. jQuery提供的存储接口

    jQuery.data( element, key, value ) //静态接口,存数据jQuery.data( element, key ) //静态接口,取数据 .data( key, valu ...

  8. Quartus II 14.0正式版 下载链接和破解器

    Windows版本 必装组件: Quartus II http://download.altera.com/akdlm/software/acdsinst/14.0/200/ib_installers ...

  9. ASP.NET框架获取数据字典数据做成树的格式

    private List<TreeEntity> treeList = new List<TreeEntity>();//创建一个树的List集合 public ActionR ...

  10. EasyUI学习笔记(1)----Tree控件实现过程中.NET下无法访问json数据的解决办法

    直接调用官网的Demo中的方法 , 将json数据存储在同目录下,但是在运行之后树没有出现,用FireBug调试,错误如下 不允许访问json数据,刚开始以为是权限不够,然后又给解决方案所在的文件夹设 ...