關於共用體存儲的問題探討

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位)來移動。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章