【C語言】整型在記憶體中的儲存

DanteKing 發表於 2021-06-30

整型在記憶體中的儲存

1.整型的歸類

  1. char
  2. short
  3. int
  4. long

以上都分為有符號(signed)與無符號(unsigned)的型別



2.原碼、反碼和補碼

2.1 定義

計算機在表示一個數字時,是採用二進位制的方式,所以為了準確表示一個數的正負,每一個有符號數都將其最高位視作是符號位,最高位為0表示正數,最高位為1表示負數。我們接下來以有符號整型int的數字進行分析。

一個有符號整數由符號位+數值位組成,數值位是其最高位,分別以0/1表示正/負

對於正數來說,反碼補碼都與原碼相同;
對於負數來說,符合以下3條規則:

  1. 原碼:將十進位制數字直接翻譯為二進位制數
  2. 反碼:原碼的符號位不變,其他位按位取反
  3. 補碼:反碼+1

而對於整型來說,整型在記憶體中實際上是以補碼的形式進行儲存的。

2.2 補碼的意義

有的同學可能就會問了,為什麼計算機要發展出原碼、反碼、補碼這麼多種碼呢?

這就與計算機對於整數的運算有關了。

CPU只有加法器,減法在運算時也會被視作一個數加另一個負數。考慮到整數的最高位是符號位,兩個整數中若包含負數,以原碼直接相加得到的數一定是不對的。所以問題就變成了如何使得運算簡單而精確,既要處理符號位,又要只進行加法運算,達到以某一種二進位制形式的“碼”直接相加就能得到正確結果。

下面,我們以60+(-18)為例,分別用原碼、反碼、補碼直接進行二進位制的運算。

原碼運算

  00000000 00000000 00000000 00111100( 60的原碼)
+ 10000000 00000000 00000000 00010010(-18的原碼)
-------------------------------------------
  10000000 00000000 00000000 01001110(某個數的原碼)

顯然,得到了的原碼轉化為10進位制是-78,並非正確答案42。

反碼運算

  00000000 00000000 00000000 00111100( 60的反碼)
+ 11111111 11111111 11111111 11101101(-18的反碼)
-------------------------------------------
 100000000 00000000 00000000 00101001
 擷取後32位:
  00000000 00000000 00000000 00101001(某個數的反碼)

顯然,得到了的反碼轉化為10進位制原碼是41,並非正確答案42,但是隻與正確答案相差(+1),於是,我們就想將負數的反碼+1,即變成“補碼”來進行運算,而又正數的補碼是原碼本身,這時候我們看看會怎麼樣呢?

補碼運算

  00000000 00000000 00000000 00111100( 60的補碼)
+ 11111111 11111111 11111111 11101110(-18的反碼)
-------------------------------------------
 100000000 00000000 00000000 00101010
 擷取後32位:
  00000000 00000000 00000000 00101010(某個數的補碼)

顯然,得到了的補碼轉化為10進位制原碼是42,我們得到了正確結果。

2.3 結論

綜上,我們發現,只要將兩個整數使用補碼進行運算,就不需要考慮它們的符號位了,將它們的所有位直接簡單相加即可,就能得到正確的結果。

2.4* 負數二進位制補碼的快速轉化

對於char型別整數,-1用二進位制補碼錶示為

11111111

當我們已知一個負數的二進位制補碼時,用比這個數多一位的、最高位為1、其他位全0、這裡應為9位的二進位制數

100000000

直接減去-1的二進位制補碼得

00000001

得到的數就是十進位制(-1)的絕對值,也就是1,只要加上負號,就能快速得到這個負數二進位制補碼的十進位制原碼。

原理十分簡單,一個負數的原碼加上補碼 =原碼+反碼+1 = 所有二進位制位全1再加1 = 多一位的、最高位為1、其他位全0



3. 大小端位元組序

3.1 什麼是大小端

在記憶體中,資料的大小端儲存是在位元組尺度上進行討論的

大端儲存模式:資料的低位儲存在記憶體的高地址,資料的高位儲存在記憶體的低地址

小端儲存模式:資料的低位儲存在記憶體的低地址,資料的高位儲存在記憶體的高地址

3.2 為什麼有大端和小端之分

在計算機系統中,我們通常是以位元組為單位儲存資料的,每個地址對應一個位元組。
一個位元組為8bit,但是在C語言中除了8bit的char之外,還有16bit的short,32bit的int。另外,對於位數大於8位的處理器,例如16位和32位的處理器,由於暫存器寬度大於一個位元組,那麼必然存在著如何將多個位元組安排的問題。這邊導致了大小端儲存模式的誕生。
我們以int型別的數0x01ff4218為例(兩個十六進位制位即為1個位元組),看一下在大小端下這4個位元組分別是如何分配的

  1. 大端儲存模式
    在這裡插入圖片描述
  2. 小端儲存模式
    在這裡插入圖片描述

3.3 寫一段程式碼來判斷你的機器的大小端位元組序

演算法簡單概括:擷取4個位元組大小的int整型的1個位元組的低位。若機器為大端位元組序,該位元組儲存0x00;若機器為小端位元組序,該位元組儲存0x01;

#include<stdio.h>
//實現方法1
int check1()
{
	int i = 1;
	return *(char*)&i;
}

//實現方法2
int check2()
{
	union check
	{
		int i;
		char c;
	}ch = {1};
	return ch.c;
}

int main()
{
	int ret = check1();
	if (ret == 1)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}
	return 0;
}

4.參考文獻

  1. C Primer Plus, 第六版, p494