首頁 > 軟體

C語言中資料如何儲存進記憶體揭祕

2022-08-18 14:00:38

記憶體簡單介紹

大家肯定經常聽說記憶體這個詞,記憶體到底是什麼呢? 在計算機中,程序都要載入進記憶體中,也是我們各種資料的流通途徑,C語言中,大家肯定都知道指標變數,指標變數中儲存的就是記憶體的地址,那麼,什麼是記憶體的地址呢?

記憶體的單位是位元組

對於32位元的機器,有32根地址線,每根地址線在定址時,產生的高低電壓分別為0/1,那麼32根地址線產生的地址就會是

00000000000000000000000000000000

00000000000000000000000000000001

00000000000000000000000000000010

11111111111111111111111111111111

這裡就有2^32次方個地址

大家應該知道,還有64位元的機器,64根地址線又有多少個地址呢,大家可以計算一下

在32位元的機器上,地址是32個0或者1組成二進位制序列,那地址就得用4個位元組的空間來儲存,

所以一個指標變數的大小就應該是4個位元組。 那如果在64位元機器上,有64個地址線,那一個指標變數的大小是8個位元組,才能存放一個地址。

這串編號就是記憶體單元的地址,就像酒店的房間號一樣,對應著記憶體中的一個位元組大小的房間

我們在vs中來看一下

這裡是以十六進位制的方式展示的,大家也知道,32個數位看起來太長了。

整數與字元在記憶體中的儲存

關於c語言中的資料型別,大家在寫了這麼多程式碼後肯定也很清楚了,C語言中有整型、浮點型、字元型、等等

我們來研究一下整數在記憶體中是如何儲存的

大家都知道,定義變數,會在記憶體中開闢空間來儲存

int a = 20;

int型別在vs中佔據4個位元組的空間,那麼如何儲存呢?

這就涉及到原碼反碼二補數的概念

  • 計算機中的整數有三種2進位製表示方法,即原碼、反碼和二補數。
  • 三種表示方法均有符號位和數值位兩部分,符號位都是用0表示“正”,用1表示“負”,剩下數值位
  • 正數的原、反、二補數都相同。
  • 負整數的三種表示方法各不相同。

原碼:

直接將數值按照正負數的形式翻譯成二進位制就可以得到原碼

反碼:

將原碼的符號位不變,其他位依次按位元取反就可以得到反碼

二補數:

反碼+1就得到二補數。

對於整型變數來說,記憶體中存放的其實是二補數

使用二補數,可以將符號位和數值域統一處理,加法和減法也可以同一處理,因為CPU只有加法器

eg:

int a = 20;
//原碼 : 直接寫二進位制    00000000000000000000000000010100
//反碼--二補數  -- 正數的原反補相同
int  b =  -5;
//原碼:   10000000000000000000000000000101
//反碼:符號位不變,按位元取反
//        11111111111111111111111111111010
//二補數:反碼+1
//        11111111111111111111111111111011  --   -5的二補數

上邊可以看見-5的二補數是11111111111111111111111111111011 ,我們如何確認呢?

轉換成16進位製為fffffffb

大家可以看到確實是使用二補數儲存的,但是為什麼是倒著儲存的,後邊再來說,這是由於大小端的問題

給大家舉幾個例子,不知道儲存無法分析出結果程式碼

//1.
#include <stdio.h>
int main()
{
  char a = -128;
  printf("%un",a);
  //-128 原碼:  10000000 00000000 00000000 10000000
  //-128 反碼:  11111111 11111111 11111111 01111111
  //-128 二補數:  11111111 11111111 11111111 10000000
  //存在a裡面的,因為只有一個位元組   10000000
  //所以會當做無符號整數列印 --整形提升
  //提升為  11111111111111111111111110000000
  //所以結果是一個很大的整數,大家可以嘗試一下
  return 0;
}

#include <stdio.h>
int main()
{
  char a = 128;
  //128的原碼反碼二補數 : 00000000000000000000000010000000
  //存在a裡的:10000000
  //整形提升 :11111111111111111111111110000000
  //所以結果還是那個很大的整數
  printf("%un",a);
  return 0;

例子就簡單給大家舉到這裡,大家一定要記住整數在記憶體中是以二補數的形式儲存的

字元儲存的是ASCII碼,所以和整數同

浮點數在記憶體中的儲存

我們來看一下浮點數在記憶體中的儲存

拋磚引玉:

#include <stdio.h>
int main()
{
    int n = 9;
    float *pFloat = (float *)&n;
    printf("n的值為:%dn",n);
    printf("*pFloat的值為:%fn",*pFloat);
    *pFloat = 9.0;
    printf("num的值為:%dn",n);
    printf("*pFloat的值為:%fn",*pFloat);
    return 0;
}

上面程式碼的列印結果到底是什麼呢?

是不是非常出乎大家的意料呢,這裡就可以看出,浮點數的儲存肯定和整數是不同的。那浮點數到底咋存的呢?

根據國際標準IEEE(電氣和電子工程協會) 754,任意一個二進位制浮點數V可以表示成下面的形式:

  • (-1)^S * M * 2^E
  • (-1)^S表示符號位,當S=0,V為正數;當S=1,V為負數。
  • M表示有效數位,大於等於1,小於2。
  • 2^E表示指數位。

看得很迷糊,直接上例子

v = 5.5

= 101.1 --二進位制表示

= 1.011 * 2 ^2 – 科學記數法表示

因為是正數 s = 0

m = 1.011

e = 2

IEEE 754規定:

對於32位元的浮點數,最高的1位是符號位s,接著的8位元是指數E,剩下的23位為有效數位M。

IEEE 754對有效數位M和指數E,還有一些特別規定

1=<M <2 ,所以M可以寫成1.xxxxx 所以可以捨去1 ,只儲存xxxxxx

IEEE 754規定,在計算機內部儲存M時,預設這個數的第一位總是1,因此可以被捨去,只儲存後面的xxxxxx部分。比如儲存1.01的時候,只儲存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的,是節省1位有效數位。以32位元浮點數為例,留給M只有23位,將第一位的1捨去以後,等於可以儲存24位元有效數位

對於指數E,E是一個無符號整數,但是科學記數法指數是可以出現負數的,所以

IEEE 754規定了偏移量,如果E為8位元,則加上127 ,如果E為11位,則加上1023

我們舉個例子

float f = 8.5f;
//二進位制 : 1001.1
//科學計數法表示: 1.0011*2^3
//S = 0 M = 1.0011  E = 3
//儲存進去的應該是:
    0 10000010 00110000000000000000000
//我們可以驗證一下
        轉換成16進位制
 0100 0001 0001 1000 0000 0000 0000 0000
//41 18 00 00

我們來看一下程式碼

和我們想的一樣

我們再來舉一個負數的例子

float t = -3.5f;
//二進位制: 11.1
//科學記數法: 1.11*2^1
//S = 1 M = 1.11 E = 1
//儲存:
1  10000000 11000000000000000000000
//轉換成16進位制
1100 0000 0011 0000 0000 0000 0000 0000
c0 60 00 00

那我們從記憶體中讀取出來的二進位制位如何解析成浮點數呢

關於E,有三種情況

E不全為0或不全為1

這時,浮點數就採用下面的規則表示,即指數E的計算值減去127(或1023),得到真實值,再將有效數位M前加上第一位的1

2.E全為0

這時,浮點數的指數E等於1-127(或者1-1023)即為真實值,

有效數位M不再加上第一位的1,而是還原為0.xxxxxx的小數。這樣做是為了表示±0,以及接近於 0的很小的數位。

3.E全為1

這時,如果有效數位M全為0,表示±無窮大(正負取決於符號位s);

我們來分析一下最開始的題目

浮點數和整數的儲存就介紹到這裡了,有哪裡不清楚的朋友可以私信我

大小端儲存模式及簡介

上邊有一個懸念,為什麼是倒序儲存的

那什麼是大端小端呢?

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

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

那為什麼會有大小端呢?

記憶體中以位元組為單位,但是比如int 是4個位元組,那如何安排這個4個位元組呢?就導致了大小端儲存模式

例如:一個 16bit 的 short 型 x ,在記憶體中的地址為 0x0010 , x 的值為 0x1122 ,那麼 0x11 為高位元組, 0x22 為低位元組。對於大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式,剛好相反。我們常用的 X86 結構是小端模式,而 KEIL C51 則 為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬體來選擇是大端模式 還是小端模式。

那我們如何測試當前電腦是哪種儲存模式呢?

int main(void)
{
	int a = 0x11223344;
	return 0;
}

我們偵錯開啟記憶體看一下

很明顯,資料的低位儲存在記憶體低地址中,所以為小端儲存模式。

我們能不能寫一個程式,直接告訴我們大小端呢?

我們來分析一下

我們來看一下程式碼

int decide_byte_orde()
{
    int i = 1;
    return *(char *)&i;
}

我們來測試一下

總結

深入理解資料的儲存是非常有必要的,我們之後碰到很多問題都會豁然開朗,大家一定要好好研究一下

到此這篇關於C語言中資料如何儲存揭祕的文章就介紹到這了,更多相關C語言資料儲存內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com