小言_互联网的博客

手绘知识点——指针的类型vs指针所指向数据的类型

305人阅读  评论(0)

前天就该写这篇了,无奈杂事缠身,一拖再拖……此篇博客并不在计划以内,只是看前篇博客的评论时发现了自身代码的一个错误,就是定义指向变量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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场