关于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. leetcode796

    public class Solution { public bool RotateString(string A, string B) { string temp = A; int len = A. ...

  2. ubuntu下编译ffmpeg并用eclipse调试

    一.下载ffnpeg源码 下载地址:http://ffmpeg.org/download.html 二.解决版本问题 可能之前你编译过ffmpeg,或者装过相关的库,那都要先卸载掉,否则用的时候会报一 ...

  3. linux上的第一个c语言程序

    1.编辑源文件 输入命令如下: root@ubuntu:/home/amy# vi hello.c 文件内容如下: #include<stdio.h> int main() { print ...

  4. Docker学习笔记_进入正在运行的Docker容器

    如何进入正在运行的Docker容器? 这里记录一种方法. 1.先查看container ID,并确认这个容器已经启动 docker ps -a       #列出懿创建的所有容器 docker ps ...

  5. Java后端WebSocket的Tomcat实现(转载)

    一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通 ...

  6. Apache htcacheclean命令

    一.简介 htcacheclean可以用于将mod_disk_cache的磁盘缓冲区占用的空间保持在一个合理的水平.这个工具可以手动运行也可以作为后台守护进程运行.当作为守护进程运行的时候,它将每隔一 ...

  7. 在Windows 8上安装SQL Server2012

    SQL Server 2012 的安装方法跟2008差不多,基本上都是点击下一步,不过在安装的时候可能会进度条一直停留在“正在启动操作系统功能”NetFx3””处不动,出现这个问题的原因是在Windo ...

  8. Reportng 的测试报告在 Jenkins 中显示不全

    通过Jenkins执行接口测试生成测试报告,用Jenkins的web服务打开html显示不全. 环境: Jenkins版本:1.651.2 Jenkins发布在mac上面 使用的是testng管理测试 ...

  9. Sql语句摘要

    1.分批更新数据库 declare @x intset @x=1 while(@x<=51) begin begin tran update UserFavorite set UserFavor ...

  10. (转)mongodb学习(翻译1)

    原文地址:http://www.cnblogs.com/Johnzhang/archive/2013/09/10/3313582.html 学习mongodb,试着翻译写,英语能力有限,希望大家指正, ...