1. 程式人生 > >挑戰408——組成原理(4)——強制型別轉換

挑戰408——組成原理(4)——強制型別轉換

本來這部分內容考綱沒有明確指出來的,但是往年的試題卻是及其熱門,可以出選擇題,也可以出大題結合暫存器部分的知識考。所以這不僅僅可以看出一個人的語言功底,同時也可以看出對補碼反碼原碼等的熟悉程度。 所以,在學習這個之前最好先回顧一下之前講的內容, 挑戰408——組成原理(2)——二進位制數和十六進位制數 挑戰408——組成原理(3)——原碼,補碼,反碼 好了言歸正傳,我們先來看看C語言中的基本資料型別(別問我為啥就強調C語言,你學這門課偏硬體):

基本資料型別

  1. 整型(int):即定點整數,在暫存器中一般用補碼錶示,其最高位代表符號位,一般是4個位元組。具體的位數跟變異平臺有關。
  2. 無符號整數(unsigned)
    :無符號,即不考慮資料位,二進位制碼錶示的數就是其值。一般用補碼錶示。
  3. 長整型和短整型(long short):用補碼錶示,這只是位數不同罷了(一個長一個短)。
  4. 單精度浮點數和雙精度浮點數(float double):就是我們平時說的小數點會移動的小數,前面的32位,後面的是64位。

資料間的保留,當計算記過超出機器所能表示的範圍的時候,就會發生“溢位現象”。此時面臨一個問題,那麼是丟掉前面的N位還是丟掉後面的N位呢?一般我們選擇保留後面的N位,丟掉前面的N位,若丟掉後發現不能表示正確的結果,說明產生溢位,還有一種情況就是不受影響了。

十進位制轉二進位制(程式碼)

在進入正題之前我們先介紹一下十進位制跟二進位制怎麼轉換(原諒我是個程式碼迷,就喜歡驗證一下)。程式碼奉上:

#include <iostream>
#include <cstdlib> //引入這個標頭檔案,使用itoa函式,因為這是C語言中的一個古老函式
#include <string>
using namespace std;
int main() {
	char array[64];
	int number = -4321;
	/*
     *itoa函式原型:char* itoa(int value,char *string,int radix)
	 *用法:將輸入的數轉換為相應進位制的數,radix,對應的進位制
	 */
	itoa(number,array,2);
	printf("integer = %d\n string = %s\n", number, array);
	return 0;
}

結果如下: 在這裡插入圖片描述 我用的是C++的環境編寫的,有人可能會問,那為什麼不用標準的COUT來呢?其實在精準度方面,C語言的printf函式確實是比C++做的好點。不信?那就試試下面這段程式碼。只是改變了一下輸出的方式:

#include <iostream>
#include <cstdlib> //引入這個標頭檔案,使用itoa函式,因為這是C語言中的一個古老函式
#include <string>
using namespace std;
int main() {
	char array[64];
	int number = -4321;
	/*
     *itoa函式,原型:char* itoa(int value,char *string,int radix)
	 *用法:將輸入的數轉換為相應進位制的數,radix,對應的進位制
	 */
	itoa(number,array,2);
	cout << "number = " << number << endl;
	for (int i = 0; i < 64; i++)
	{
		cout << array[i];
	}
	return 0;
}

那麼會出現什麼結果?自己去試試吧,話說到這裡。

轉換過程

同類型之間的轉換

進入正題,先看下面的一段程式碼:

#include <iostream>
#include <cstdlib> 
#include <string>
using namespace std;
int main() {
	short x = -4321;
	unsigned short y = (unsigned short) x;
	printf("x = %d, y = %u\n",x,y);
}

結果如下: 在這裡插入圖片描述 我們前面說過,它們都是在暫存器中以補碼的形式存放的,所以,我們把這兩個數寫成補碼的方式(擷取後16位): 在這裡插入圖片描述 我們驚奇的發現,這裡的二進位制表示居然一模一樣。但是結果卻不同,原因是X的第一位解釋成了符號,而後面的才解釋成為真值。(這裡不知道有沒有較真的人發現,-4321的補碼沒有錯,但是61215的補碼總覺得怪怪的?哈,肯定是不記得了正數的反碼 = 補碼 =原碼,負數的才是取反後加一)。 所以,強制型別轉換實際上是位值不變,只是改變了解釋這些位的方式。

#include <iostream>
#include <cstdlib> 
#include <string>
using namespace std;
int main() {
	unsigned short x = 65535;
	short y = (short) x;
	printf("x = %d, y = %u\n",x,y);
}

同理分析這一段程式碼就一目瞭然了。

不同位元組型別之間的轉換

我們知道,不同型別的資料往往其佔有的位元組大小不一樣,比如: 在這裡插入圖片描述

在結合上面的第一次的程式碼,-4321前面的16位都是1又是為何?不夠的數都用1來湊?顯然不對。

#include <iostream>
#include <cstdlib> 
#include <string>
using namespace std;
int main() {
	int x = 165537, u = -34991;
	short y = (short)x;
	short v = (short)u;
	printf("x = %d, y = %u\n",x,y);
	printf("u = %d, v = %u\n",u,v);
}

當資料太大,用二進位制不好表示的時候我們選擇用16進位制(在之前提到過),分別是0x000286a1,0x86a1,0xffff7751, 0x7751,可以看出大位元組轉向小位元組的轉換的規則是:低位直接賦值(賦幾位就看你的資料佔幾位,比如short佔2位元組,16位,一個16進位制數代表4位2進位制數),高位直接截斷

那麼顯然,反過來,小位元組轉向大位元組那就得擴充套件位數了。比如:

#include <iostream>
#include <cstdlib> 
#include <string>
using namespace std;
int main() {
	short x = -4321;
	int y = x;
	unsigned short u = (unsigned short)x;
	unsigned int v = u;
	printf("x = %d, y = %u\n",x,y);
	printf("u = %d, v = %u\n",u,v);
}

輸出的數用16進製表示我就不說了,結論:從短字長到長字長的轉換,相應的位值不變,向高位補充的數為符號位,所以-4321前面的16位都是1,因為它符號位是負號,如果是正號,那就變成0.

這部分內容考試必考,2分選擇題左右。後面刷題的時候回再說