C \ C++中整型資料都是有資料範圍的,對溢出的資料處理機制一般是資料值和資料範圍值進行求模處理,求模可以這麼了解,資料描述是一個資料描述範圍最小值到最大值的一個閉環循環,求模後的值會仍在這個閉環範圍内,例如鐘表,可用1~12來一個閉環來描述,13則就是從新回到1(13 % 12)。
在C\C++中,一個整型資料val,其目前類型下的描述值可用如下公式計算:
描述值 = (val - 目前類型最小值) % 資料範圍 + (目前類型最小值)
這個計算對于資料溢出和不溢出都是适用的。
以short 和 unsigned short類型來說明。
(1)short int資料類型
short資料占用2個位元組,則資料範圍為-32768~32767,共計65536個數,則一個short val,其實際描述值realVal為
realVal = (val - (-32768)) % 65536 + (-32768)
示例代碼:
short sia = -;
short sib = (- + ) % - ;
printf("not overflow\n");
printf("sia = %hd\n", sia);
printf("sib = %hd\n", sib);
sia = -;
sib = (- + ) % - ;
printf("\noverflow\n");
printf("sia = %hd\n", sia);
printf("sib = %hd\n", sib);
sia = ;
sib = ( + ) % - ;
printf("\noverflow\n");
printf("sia = %hd\n", sia);
printf("sib = %hd\n", sib);
運作結果:
說明:
(1)在指派操作時,如
sia = -32769
,-32769是先作為一個常量值存放在記憶體中,這個數值是沒有越界的概念的,就是二進制值,隻是在指派操作時才進行了對應的資料轉換;
(2)從運作結果可見,該處理機制對資料溢出和不溢出得到的結果都是适用的;
(3)聯想資料閉環循環,如程式中32768,超過short型上限,其溢出一個數,作為一個資料閉環,則直接跳轉到了-32768,也就是short型下限值。
(2)unsigned short資料類型
處理機制同上,對于一個unsigned short資料val,實際存儲值realVal為
realVal = (val - 0) % 65536 + 0
示例代碼:
unsigned short uia = ;
unsigned short uib = % ;
printf("not overflow\n");
printf("uia = %hu\n", uia);
printf("uib = %hu\n", uib);
uia = ;
uib = % ;
printf("\noverflow\n");
printf("uia = %hu\n", uia);
printf("uib = %hu\n", uib);
運作結果:
(3)double和float的資料精度
double和float資料有精度範圍,對于float和double類型的精度範圍和描述方式,可參考下面的兩篇文章:
http://www.linuxidc.com/Linux/2012-07/65987.htm
http://blog.sina.com.cn/s/blog_6ebd49350101gdgo.html
一旦超過精度範圍,就不能精确的描述該資料,不同的處理器對不能精确描述的部分的處理機制可能是不同的,這也就導緻了在不同的平台上為什麼一套代碼的運作結果會不一緻,最近在調試程式時,出現PC上(intel處理器)和手機中(arm處理器)中運作結果不一緻的現象。在單步調試時發現在超出資料精度後的處理機制不一緻而導緻的。
(4)資料類型轉換
當unsigned型資料類型之間轉換時,低範圍向高範圍轉換時不會出現溢出現象,高範圍向低範圍轉換時,則可以按照上述方式得到要轉換的值。
當unsigned和signed類型之間進行轉換時,正數部分的轉換同上,由負數轉向unsigned型,也就是直接讀取記憶體中的資料,然後進行格式轉換即可,例如
short val = -;
unsigned short uval = val;
unsigned int uival = val;
資料存儲是以反碼表示,short型-1的表示為1111 1111 1111 1111(補碼形式),轉換為unsigned short時不需要擴充位元組,直接讀取位元組内容即為1111 1111 1111 1111,這裡沒有符号位,則uval = 65535,同理轉換為unsigned int時,需要擴充位元組,為了盡量避免轉換錯誤會先進行同類型資料轉換,即先轉成int,然後在轉成unsigned int,有如下處理過程:
int temp = val;
unsigned short uval = temp;
轉成int時會擴充位元組,擴充為0xFFFFFFFF,在轉換為unsigned short時,則uval = 4294967295。
上述轉換也可以用上面的公式來計算得到
示例代碼:
short iSVal = -;
unsigned uSVal = (- - ) % + ;
unsigned uVal = (- - ) % + ;
printf("iSVal = %hd\n\n", iSVal);
printf("iSVal change unsigned short val %hu\n", iSVal);
printf("uSVal = %hu\n", uSVal);
printf("\niSVal change unsigned int val %u\n", iSVal);
printf("uVal = %u\n", uVal);
運作結果:
可見在進行類型轉換時均可以使用上面的公式來得到結果!不過不建議unsigned和signed之間進行類型轉換。
(5)其他
不同處理器之間可能對資料越界問題的處理機制可能會不一樣,前面說過arm和intel對float和double型資料超過其精度範圍的資料處理機制就不同,同樣在調試中發現arm處理器對int資料越界後會将值設定為int型最大值,這個比較奇怪了。
是以在程式設計時還是要預估資料範圍來設定資料類型,以防止程式運作錯誤,必要時要提升資料範圍。