前天就该写这篇了,无奈杂事缠身,一拖再拖……此篇博客并不在计划以内,只是看前篇博客的评论时发现了自身代码的一个错误,就是定义指向变量double型变量b的指针pb的时候,前边类型写成了int *,应该是double *,当时不知道是啥情况,这么明显的错误竟然没有发现,但是编译也并没报错,主要还是输出的时候设计到的pb太少,这可能就是天意吧,那么今天就仔细探究一下这一小块的知识点吧~
先放上前篇博客的地址,有需要的朋友可以自查:
https://blog.csdn.net/beyond9305/article/details/101165585
先说下核心要义吧,我们此时主要探究的就是int和double变量及其指针变量格式化输出时的一系列骚操作,先截取上篇博客的部分代码做个大前提:
int a = 1;
double b=3.1415;
int *pa = &a;
double *pb = &b;
这是最基本的普通变量和指针变量定义及初始化(就是这样我上次也出错了,好尴尬~),此时我们可以假设四种情况分别进行实验,看看会出现什么神奇的结果
1.
printf("*pb的值:%f\n", *pb);
pb的类型为double *,所致数据b的类型为double,格式化输出为%f,看结果:
没啥问题,这里需要说明一下%f默认输出6位小数,如果需要更高的精度可以自行设定
2.
printf("*pb的值:%d\n", *pb);
依然是输出pb所指的double类型变量b的值,但格式化输出控制符为%d,即输出为整型,看结果:
这是个啥,乱码了吗~我一开始也这么认为,但大家仔细看看这个值,我们后边再说
3.
int *pb = &b;
printf("*pb的值:%d\n", *pb);
和2相比我依然以整型输出*pb,但把前边定义的pb类型改为int *,现在就成了这么一种情况:让int *型的指针pb指向double型变量b,并以整型格式化输出,看结果会是啥样的:
和前边的输出是一样的,看来这个数字并非偶然,或许并不是乱码,而是一个有重要意义的神奇数字
4.
int *pb = &b;
printf("*pb的值:%f\n", *pb);
最后一条想必大家也想到了,如上所示:让int * 型的指针变量pb指向double型变量b,并以浮点型格式化输出,给大家三秒猜猜结果是个啥,好,时间到~
这个结果惊不惊喜,意不意外~
咱们的风格是力求精简,大白话接地气,先来看一张图:
这是3.1415在内存中的存储形式 , 感兴趣的朋友可以自行查阅一下浮点型数据在内存中的存储要求 , 这里简单说一下就是在C
语言中单精度float和双精度double在格式化输出时都可以用%f , 系统会自动将float提升为double , 大家也知道在32及更高版本系统中float占4字节,而double占8字节 , 这样当我们存储一个double型的变量时就需要64位 , 具体分配为 : 符号位(1)+阶码(11)+尾数(52)
于是乎3.1415在内存中的表示就如上图所示 , 这其中涉及到尾数的整数部分的1不算入最终结果 , 阶码移码的转换等 , 同志们可重点关注一下这一块 , 都是基础知识
我们先来看第二种情况和第三种情况 , 输出是一样的,对于前者大家可以这么理解,我们在内存中开辟了连续的八块空间即八个字节大小的区域存放double变量b,然后当printf整型格式化输出时我们只能获取低位的4个字节(可以想象成输出时格式化控制符为%d就告诉系统我们要输出的是一个整型数据占据4个字节 , 而内存存放数据的原则是先低后高 , 即先存放低位数据再存放高位数据 , 所以我们得到的4字节数据是double变量b的低32位数据) , 具体的看下图吧 :
为了省事我就没有把对应的数据填进去~~~
大家可以算一下,从低位开始存放,那么我们第二种和第三种情况所得到的都是低32位数据,按整型数据去解析的话正好是数字-1065151889(这里涉及到原码补码,如果大家感兴趣可以看一下我四年前的文章,感觉讲的还算比较详细:https://blog.csdn.net/beyond9305/article/details/48805919)
至于第四种情况输出0也不难理解,整型变量指针pb只知道他指向的内存区域为4字节(即首地址+偏移量,这里的首地址即pb的值,也就是变量b的地址,而偏移量则是连续的4字节内存区域),但很可惜这片区域存放的并不是一个整型数据,而是部分的浮点型数据,这就很容易混乱,输出稀奇古怪的结果,那这里为何会输出0呢?
由于pb认为自己所控制的区域是一个整型数据,所以按正常输出的应该是0xXXXXXXXX,我们以十六进制表示比较方便,共八位十六进制数,等价于32位二进制数,但输出的时候是以64位浮点型格式输出的,那剩下的32位怎么补呢,有的朋友会想到那就继续在内存中往高位取32位数据不就得了,但是这样的话很容易得到奇异值,因为你不知道再往高处走会是存放的啥东西,这一点我们上篇也提到了,如果你先后定义两个变量,那二者在内存中是不一定连续存放的,这是由于系统自身的保护机制,有利于debug时保存断点,大概是这意思吧,总是就是不能胡乱往高位取数据,毕竟这不是你的地盘,胡乱取数据很可能造成不必要的麻烦。
那么怎么办呢,最方便的就是在高位添加0了,这样又能凑够64位,又不影响数据的大小(当然这样也会存在个问题,如果我们在前边加0的话,那对于浮点数来说最高位为符号位,就一直为正数了。。)对应本例就是前边的0xXXXXXXXX变成了0x00000000XXXXXXXX,大家可以算一下,把这个数按照浮点数的存储规则,拆分为符号位+阶码+尾数样式,那么得到的数是什么呢,直接说结果吧,是一个小数点后有1000多个0的小数,具体的计算可以参照前边提到的3.1415的转换形式,其实这就是一个逆运算而已,所以最后这个小数以十进制的格式输出后就是大家看到的0.000000,我们前边也说了,默认的浮点型输出精度为六位,其真正面目是后边还有一堆0~~有图有真相,我们输出小数点后300位:
总之大家只要记住,咱们还是规规矩矩地来,整型和浮点型不要相互穿插,不然会得到意料之外的奇葩结果……
今天的讨论有咩有让大家想到强制数据类型转换呢,不过其截取的是高位数据而已,低位的直接丢弃掉了,关于这点感兴趣的可以自查哦,了解其底层原理还是很有意思的
大概就是这些内容了吧,可能又啰嗦了,哎,控制不住啊,有些想法一落实到语言上就显得很无力,各位应该能懂~
最后说个小插曲吧,一开始计算3.1415的二进制时是纯手算的:
然而效果并不好,之后就想到了。。。
现代科技是多么地方便……
—如果本篇内容对你有一点点帮助,请点个赞或者收藏关注一下,让我们一起努力—
转载:https://blog.csdn.net/u014483914/article/details/101623325