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