C語言中數據如何存儲進內存揭秘

內存簡單介紹

大傢肯定經常聽說內存這個詞,內存到底是什麼呢? 在計算機中,進程都要加載進內存中,也是我們各種數據的流通途徑,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("%u\n",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("%u\n",a);
  return 0;

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

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

浮點數在內存中的存儲

我們來看一下浮點數在內存中的存儲

拋磚引玉:

#include <stdio.h>
int main()
{
    int n = 9;
    float *pFloat = (float *)&n;
    printf("n的值為:%d\n",n);
    printf("*pFloat的值為:%f\n",*pFloat);
    *pFloat = 9.0;
    printf("num的值為:%d\n",n);
    printf("*pFloat的值為:%f\n",*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語言數據存儲內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: