標籤:java 使用 strong 資料 art 代碼 div ar
電腦中儲存和處理的資訊是以二進位訊號表示的。單個的位不是是很實用,而將這些位 組合在一起,加上某種解釋,即給不同的可能位元模式賦予含義,我們就行表示怎樣有限集合的元素,即實現各種資料結構。電腦中使用8位的塊稱之為位元組作為最小的可定址的儲存空間單位,機器級程式將儲存空間視為一個很大的位元組數組,稱為虛擬儲存空間。儲存空間的每一個位元組都有一個唯一的數字來標識,稱為地址,全部可能地址的集合稱為虛擬位址空間。
c語言中的指標,指標的值為某個儲存塊的第一個位元組的虛擬位址。C編譯器將每一個指標和類型資訊結合起來,依據指標值的類型也就是聲明的指標類型,來產生不同的機器級代碼來訪問儲存在指標所指向位置出的值每一個程式對象能夠簡單的視為一個位元組塊,而程式本身就是一個位元組序列。
字長,32位系統為4個位元組,64位系統為8個位元組,對於32位字長時,其能表示的虛擬位址範圍為0 - 2^32-1 ,即虛擬位址空間最大為4GB。
大端法:最高有效位元組在低地址位。
小端法:最高有效位元組在高地址位。
一般系統都使用的是大端法。
C中主要的位元運算:
^異或, 1 ^ 0 = 1 , 0 ^ 1 = 1, 0 ^ 0 = 0, 1 ^ 1 = 1 .
& 且 |或 ~取反
<< n 左移 n 位 >> n 右移n位
c中的右移有兩種,邏輯右移和算術右移,邏輯右移即在左端補0,而算術右移在左端不最高有效位的值。一般對於有符號數進行的是算術右移,
java中使用>>表示算術右移,而使用>>>表示邏輯右移。
位元運算操作符的優先順序都低於 加減 號, 所以 在算式中 一般都要用括弧 括起來。
對位元運算的一個常見使用方法是實現掩碼運算。
掩碼就是指從位元組中選出一定位的集合。
電腦系統中有三種重要的數字表示,無符號(unsigned)編碼,僅僅能表示非負的數,補碼(two‘s-complement)編碼,用來表示有符號整數,浮點數(float-point)編碼,表示實數的科學計數發的二進位版本號碼。僅僅有C系語言有無符號數這個概念,但我覺得無符號數的存在是一種長處。
這裡討論兩種編碼,無符號和補碼。
無符號數的編碼即直接將位元據轉換為10進位,就能夠得到結果。其最大值為 2 ^ w - 1,w為其位元,最小值為0.
補碼:最高有效位解釋或為負權,即 - 2 ^w ,然後將全部位乘以權值得到結果。最高有效位即為符號位。最大值為 2 ^(w-1)-1,最小值為 - 2^w.補碼的範圍是不正確稱的。
因為不同機器上的資料類型可能有不同的取值範圍,如long,在32位系統中為4個位元組,但在64位系統中為8個位元組。所以一般使用在stdint.h中定義的資料類型intN_t和unintN_t,來聲明N位的有符號數和無符號數,則會使程式在不同系統中都能夠正確執行。N一般去8,16,32,64。
有符號數和無符號數之間的轉換:
程式讀取不同類型時,僅僅是對儲存空間上的資訊以不同的方式進行解釋,而C語言進行強制轉換時,也僅僅是改變了其解釋儲存空間上位資訊的方式,並沒有改變儲存空間上的資訊本身。
所以相同位長的無符號數和有符號數直接的轉換,不改變底層的位資訊,僅僅是換了一種解釋的方法來解釋這些位。如對無符號的int 4 294 967 295轉換為有符號的int就是 -1,而有符號的int 的-1轉換為無符號的int 就是4 294 967 295。
當一個運算中 一個運算數為有符號,一個運算數為無符號數,C語言會隱式地將有符號數強制類型轉換為無符號數,,然後運行兩個無符號數之間的運算。對於標準的運算來說,這樣做不會出錯。如 c = a + b, 當中c和a為有符號int,a為-1,b為無符號int為1,則運算時,將a轉換為無符號數,也就是 4 294 967 295,然後加b,應得到的是4 294 967 296,但在位級上,因為最高位的1超過範圍而溢出,僅僅剩下32個0,則最後c的結果為0。能夠看出,這裡對普通的運算沒有影響。
但在一些關聯運算式中,則會出錯。如 -1 < 0u。u表示無符號數,這裡將-1轉換為無符號的int後,值為4 294 967 295,肯定是比0要大的,然後就出錯了。
在不同字長的整數之間進行轉換,須要進行為的擴充和截斷。
擴充:
無符號數擴充使用,零擴充:在表示的開頭加入0。
有符號數擴充使用,符號擴充:在表示中加入最高有效位的值的副本。即符號位為1時,加入若干個1。
有符號的負數擴充時,前面加入1不會對最後的數值有影響,假定擴充前為w位,擴充後為v位,擴充前的值為-i,則有擴充前除去最高位,剩下為表示的整數為j = 2^w - i,則擴充後,從符號位到原來符號位,即除去j表示資料的部分的剩餘部分表示的值為: - 2 ^(v-1) + 2^(v-2)+...+2^w ,則擴充後表示的值為 - 2 ^(v-1) + 2^(v-2)+...+2^w + 2 ^w-- i.則前面的數都能夠消去,即得到最後結果為-i。
.C中同一時候進行有無符號轉換和不同字長的資料轉換時,想轉換不同字長的資料類型,即先進行擴充或者截取,然後在使用有符號或者符號的方式來解釋資料。
截斷:
截取時即捨去高n位就可以。
整數運算:
無符號數的加法,會導致結果大於資料類型的範圍,則無符號的運算事實上是一種模運算,利用溢出機制,實現計算和模2^w。
算術運算溢出,指完整的整數結果不能放到資料類型的字長限制中去。推斷兩個數相加,如 c = a + b,是否發生溢出,僅僅要推斷 c是否 小於a或者b就可以,由於a或者b肯定是小於2^w。
補碼的加法:
兩個有符號數的w位補碼之和與無符號數之和有全然相同的位級表示,電腦使用相同的機器指令來運行無符號或有符號的加法。如之前進行的分析: c = a + b, 當中c和a為有符號int,a為-1,b為無符號int為1,則運算時,將a轉換為無符號數,也就是 4 294 967 295,然後加b,應得到的是4 294 967 296,但在位級上,因為最高位的1超過範圍而溢出,僅僅剩下32個0,則最後c的結果為0。
假設將這裡的b也看作是有符號數,其結果依舊如此且正確。
所以,我們能夠理解為,補碼的加法是 先將其轉換為無符號數,然後相加,得到的結果在轉換為有符號數。
這裡有溢出,正溢出和負溢出。
正溢出:得到的結果應該位於2^(w-1) —— 2 ^w - 1,但使用補碼轉換為有符號數後是在 0 —— - 2 ^ (w-1)這個範圍內。
負溢出:得到的結果應該在-2^w —— -2^(w-1),可是因為溢出,結果在 0 —— 2^(w-1) -1上。
其它情況為正常。
補碼的非:
即取負。對於 -2 ^(w-1)取非為 其本身。對於其它的值能夠得到正確結果。
無符號的乘法:
即計算乘積後模 2^w。
補碼的乘法:
補碼乘積的範圍在 -2^(2w-2) + 2^(w-1) 和 -2^(2w-2)之間。這裡,對於無符號和補碼的乘法運算,其位級表示依舊是一樣的,所以對於補碼的乘法是將其轉換為無符號數,然後相乘,結果模2^w。這裡對於運算的分析:
令x,y的無符號值為 a,b,其符號位值為 c,d。則 a = x + c * 2^w , b = y + d * 2 ^ w;
a * b % 2^w
= ( (x + c * 2^w )*( y + d * 2 ^ w) )%2^w
= (x * y + (y *c + x*d + c*d * 2^w)* 2^w)% 2^w
= (x*y)% 2^w
電腦上的乘法指令相當慢,一般須要10個以上的刻度。編譯器會使用一項重要的最佳化,用移位和加法運算和減法運算來取代乘以常數的乘法。如使用(x<<4)-(x<<1)來取代x*14。
電腦上的除法的速度就更加慢了,一般須要30個以上的刻度。除以2的冪能夠通過右移操作進行,對於無符號是邏輯移位,對於補碼進行算術移位。