关于共用体存储的问题探讨

1、  问题发现:

在涉及到共用体内存冲刷问题时,编程验证,初次发现下面问题:

#include "stdafx.h"

#include <iostream>

using namespace std;

uniondatatype

{

double a;

    int    b;

}x,y;

voidmain()

{   x.a=8;

    x.b=4;

    y.b=4;

    y.a=8;

    cout<<"x.a="<<x.a<<"  "<<"x.b="<<x.b<<endl;

    cout<<"y.a="<<y.a<<"  "<<"y.b="<<y.b<<endl;

    cout<<"the size of union is:"<<sizeof(datatype)<<endl;

    cout<<"the size of a is:"<<sizeof(double)<<endl;

   cout<<"the size of b is:"<<sizeof(int)<<endl;

    system("pause");

}

运行结果:

很直观的疑问在于,当对共用体中x.a赋值为double型8后,再给x.b赋值为int型4,在最后输出应该是以后赋值的为准,而先前赋值的8应该被冲刷掉,但仍然能打印出来。本来原先的数据是没必要再去关心的,但还是一探究竟:

2、问题分析与解决:

上网查找double型(或float)数据以及int型在内存中的存放方式:

  目前C/C++编译器标准都遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。下面是具体的规格: 
           ````````符号位    阶码      尾数     长度 
  float            1              8           23       32 
  double          1          11          52      64  
  由于通常C编译器默认浮点数是double型的,下面以double为例: 
  共计64位,折合8字节。由最高到最低位分别是第63、62、61、……、0位: 
  最高位63位是符号位,1表示该数为负,0正; 
  62-52位,一共11位是指数位; 
  51-0位,一共52位是尾数位。 

  在浮点型的数据在内存中的存放方式比较特殊,我们将其原先的整形8改为8.4进行探讨:

x.a=8.4;

    x.b=4;

    y.b=4;

    y.a=8.4;

运行结果:

,从这里可以看到浮点数和整形数的明显区别。可以看到在x中,后来的int型对前面的double数据产生影响,使其显示为近似值,而在共用体y中,后来的double直接覆盖掉先入的int型,y.b显示的是随机数。但在这里,还是不知道是如何影响的,打算打开内存,一看究竟:

// 内存打印.cpp :定义控制台应用程序的入口点。

#include "stdafx.h"

#include "stdafx.h"

#include <iostream>

#include <bitset>

using namespace std;

 

uniondatatype

{

   double a;

   int   b;

}x,y;

 

voidwatch_mem(char* addr,intlen)

{

    for(int i = 0;i <len;++i)

    {

       printf("%X\t",*(addr+i));

    }

    printf("\n");

    union datatype* data = (uniondatatype*)addr;

    printf("a:%f\tb:%d.\n\n",data->a,data->b);

}

int_tmain(int argc, _TCHAR* argv[])

{

   x.a=8.4;

    watch_mem((char *)&x,sizeof(datatype));

   x.b=4;

    watch_mem((char *)&x,sizeof(datatype));

   y.b=4;

    watch_mem((char *)&y,sizeof(datatype));

   y.a=8.4;

    watch_mem((char *)&y,sizeof(datatype));

   cout<<"x.a="<<x.a<<"  "<<"x.b="<<x.b<<endl;

   cout<<"y.a="<<y.a<<"  "<<"y.b="<<y.b<<endl;

   cout<<"the size of unionis:"<<sizeof(datatype)<<endl;

   cout<<"the size of a is:"<<sizeof(double)<<endl;

   cout<<"the size of b is:"<<sizeof(int)<<endl;

   system("pause");

}

运行结果:

可以看到,浮点型的8.4在内存中存放为:

FFFFFFCD FFFFFFCC FFFFFFCC FFFFFFCC FFFFFFCCFFFFFFCC 20 40,接着输入int型的4,只占用掉前面的4个字节,变成:4 0 0 0 FFFFFFCC FFFFFFCC 20 40,这时再要打印b时,只提取了自己存放位置的数据,为4 0 0 0 ,在c++编译器中,数据按照高位存储,倒过来就是0 0 0 4,因此最后显示为4.而原先的数据后四位仍然存在,当调用显示x.a时,将整个数据装换成double型,这里我们以8.4为例,看double是如何存储的:

8.4,分解为整数部分和小数部分,整数部分8,二进制位:1000,小数部分:0.4化为二进制:011001100110……这个就是小数表示在计算机中的无限循环问题,无法精确表示,根据double的精度,小数最后保留53位。

8.4(10)=1000.01100110 0110……(2)=(浮点型)1.0000110 0110 0110…*2^3

现在看,整个浮点型double数据格式:阶符(1位)+阶码(11位)+尾数(53位)

阶码,一共11位,可以表示范围是-1024   ~   1023。因为指数可以为负,为了便于计算,规定都先加上1023,在这里,3+1023=1026,表示成二进制:10000000010:;符号位为0(0正1负),在这里还有个问题就是隐藏技术,我们将科学计数表示的数小数点前的1隐藏,不存储,所以整个数据最后为:

01000000 00100000 11001100 11001100 11001100 11001100 11001100 11001100

转换为十六进制:

40 20 CC CC CC CC CC CC

在内存存储时,vs编译器采用高位存储(大端存储)方式,倒过来:

CC CC CC CC CC CC 20 40;

 

和运行结果比较:FFFFFFCDFFFFFFCC FFFFFFCC FFFFFFCC FFFFFFCC FFFFFFCC 20 40,可以看出在有效数据部分基本一致,但多出一些FFFFFF,继续打开调试内存,


发现里面存放的又是转换成的ascii码值,两者为何有别,有点糊涂了。

转为十六进制:


进一步打开内存:

看到在内存中存储的确实是我们转换过的数据,而没有FFFFFF。感觉问题不是出在内存,而是在打印函数printf。单独测试将-1打印出来,为FFFFFFFF,接着将结果按照%d打印出:


看来编译器是首先读取到CC,然后再将其认为是-52,在printf打印时,将负数按照32位表示出来并显示。

这里还有待确认的问题,就是实际内存分配时,当指针加1时,移动的位置是按照编译系统的字(32位)来移动,还是按照一个字节(8位)来移动。

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值