【C語言進階】——數據的儲存、大小端(一) (超詳細剖析+代碼解析!)

@toc 編程

image-20210809174502113


1、數據類型詳細介紹(從大方向分爲兩種類型)

1.內置類型(C語言自己庫有函數)

image-20210809144453991

2.構造類型(自定義函數)

自定義函數是系統不自帶的,經過本身的編寫後可使用的函數。通常的編程語言、工做表等均可以編寫自定義函數使用。
編寫自定義函數能夠簡化主程序,讓程序的檢查調試更方便小程序


3.類型的意義

類型的意義,能夠從如下兩個角度考慮:windows

1.使用這個類型開闢內存空間的大小(大小決定了使用範圍)markdown

好比說使用char類型建立的變量,開闢的內存空間是1個字節,使用int類型建立的變量,開闢的內存空間是4個字節編程語言

2.如何看待內存空間的視角ide

好比:
int a = 0;
float b = 0.0;
雖然 a, b都是佔用4個字節的空間,可是咱們在看待a的時候,由於其類型是int,因此咱們會把a當作整型來看待,在看待b的時候,由於其類型是float,因此咱們會把b當作小數(而非整型)來看待。函數


2、數據類型細分類

1.整形家族

image-20210809150705230

2.浮點型家族

image-20210809150722493

3.構造類型

image-20210809150756215

4.指針類型

image-20210809150806739

5.空類型

image-20210809151051623

例:翻譯

image-20210809151711181


3、整型在內存中的存儲

以前講過 一個變量的建立是要在內存中開闢空間的。空間的大小是根據不一樣的類型而決定的。設計

那接下來咱們談談數據在所開闢內存中究竟是如何存儲的?3d

1. 如何儲存

看下面這個例子:

#include<stdio.h>
    int main()
{
    int a = 3;
    int b = -1;
    return 0;
}

爲了查看a, b在內存中的存儲形式,咱們在編譯器裏面按F10進入調試,變量a,b建立後,打開內存監視器,輸入& a,& b查看a,b對應的地址及其內容。

& a:

image-20210809152043862

&b:

image-20210809152057109

數據在內存中存儲時是按二進制的補碼存儲的

展現內存的時候,爲了方便展現,顯示的是16進制數據

什麼意思呢? 咱們定義一個變量c,以16進制形式對其賦值,而後& c,能夠看到:

image-20210809154109543

c輸入的16進制形式是 11223344 ,存儲的時候是 44332211 接下來下面講:

2. 原碼、反碼、補碼

下面先來了解幾個概念︰原碼、反碼、補碼

計算機中的有符號數有三種表示方法,即原碼、反碼和補碼。
三種表示方法均有符號位和數值位兩部分,符號位都是用0表示′正」,用1表示"負」,而數值位三種表示方法各不相同。

原碼:直接將二進制按照正負數的形式翻譯成二進制就能夠。

反碼:將原碼的符號位不變,其餘位依次按位取反就能夠獲得了。

補碼:反碼 + 1就獲得補碼。

正數 原碼反碼補碼 三碼合一負數的原反補 按照上面的規則進行轉換


整數有兩種,有符號數和無符號數
有符號數-- - 符號位 + 數值位
正數 0 + 數值位
負數 1 + 數值位

int b = -1;
//10000000 00000000 00000000 00000001 - 原碼
//11111111 11111111 11111111 11111110 - 反碼
//11111111 11111111 11111111 11111111 - 補碼
//ff ff ff ff - 十六進制顯示形式

int a = 3;
//00000000 00000000 00000000 00000011 - 原碼、反碼、補碼
//0000 0000 0000 0000 0000 0000 0000 0000 0000 0011
//00 00 00 03 - 十六進制顯示形式

無符號數 unsigned --- 正整數是同樣的!(只能表示正整數!)<br/>無符號數 --- 原反補按規則計算

【C語言進階】——數據的儲存、大小端(一)     (超詳細剖析+代碼解析!)

數據存放內存中其實存放的是補碼。


3. 爲何內存中要存儲補碼?

咱們首先來看一下 1 - 1 這個例子:

①先按照原碼的方式去計算。

image-20210809154338100


②接下來用補碼來進行計算:

image-20210809154849166

在計算機系統中,數值一概用補碼來表示和存儲。緣由在於,使用補碼,能夠將符號位和數值域統一處理; 同時,加法和減法也能夠統一處理(CPU只有加法器)此外,補碼與原碼相互轉換,其運算過程是相同的,不須要額外的硬件電路。


怎麼理解補碼與原碼相互轉換,其運算過程是相同的?(如下運算,符號位均不變)

原碼-&gt;取反 + 1-&gt;補碼
補碼-&gt;取反 + 1-&gt;原碼
固然補碼到原碼也能夠是:補碼 -&gt; - 1 取反-&gt;原碼

例如 - 1:
11111111 11111111 11111111 11111111 - 補碼

補碼->取反 + 1->原碼
10000000 00000000 00000000 00000000 - 取反
10000000 00000000 00000000 00000001 - +1

補碼 -> - 1 取反->原碼
11111111 11111111 11111111 11111110 - -1
10000000 00000000 00000000 00000001 - 取反

最終獲得的結果均是:10000000 00000000 00000000 00000001


4、大小端介紹

1.什麼大端小端

大端(存儲)模式,是指數據的低位保存在內存的高地址中,而數據的高位,保存在內存的低地址中。

小端(存儲)模式,是指數據的低位保存在內存的低地址中,而數據的高位,,保存在內存的高地址中。

#include <stdio.h>
#include <windows.h>
int main()
{
    unsigned int a = -10;
    printf("%d\n", a);//%d有符號數打印
    printf("%u\n", a);//%u無符號數打印
    system("pause");
    return 0;
}

image-20210809160328654

對上述代碼能運行的解釋

存:
1000 0000 0000 0000 0000 0000 0000 1010  ->原碼
1111 1111 1111 1111 1111 1111 1111 0101  ->反碼
1111 1111 1111 1111 1111 1111 1111 0110  ->補碼
//由於是int類型,因此在內存存儲32位
即 0xfffffff6

那麼如何存呢?

image-20210809160409654

能夠看出是小端存儲。

解析:

image-20210809160815660

無符號數 unsigned --- 正整數是同樣的!(只能表示正整數!)
無符號數 --- 原反補按規則計算

數據存取過程圖解:

image-20210809160659063

2.爲何有大端和小端

爲何會有大小端模式之分呢 ?

這是由於在計算機系統中,咱們是以字節爲單位的,每一個地址單元都對應着一個字節,一個字節爲8bit。
可是在C語言中除了8bit的char以外,還有16bit的short型,32bit的long型(要看具體的編譯器)。
另外,對於位數大於8位的處理器,例如16位或者32位的處理器,因爲寄存器寬度大於一個字節,那麼必然存在着一個若是將多個字節安排的問題。
所以就致使了大端存儲模式和小端存儲模式。
例如一個16bit的short型x,在內存中的地址爲ox0010),x的值爲0×1122,那麼0x11爲高字節,0x22爲低字節。對於大端模式,就將0x11放在低地址中,即0x0010中,0x22放在高地址中,即0x0011中。小端模式,恰好相反
咱們經常使用的x86結構是小端模式,而KEIL c51則爲大端模式。不少的ARM,DSP都爲小端模式。有些ARM處理器還能夠由硬件來選擇是大端模式仍是小端模式。


筆試題:用小程序判斷當前機器的字節序(大小端)

百度2015年系統工程師筆試題:
請簡述大端字節序和小端字節序的概念,設計一個小程序來判斷當前機器的字節序

如何判斷大端、小端(字節序)呢?

思路:
int a = 1;
將a的第一個字節內容拿出來,判斷其是1仍是0,1爲小端,0爲大端。

char* 儲存一個字節

#include <stdio.h>
int check_sys()
{
 int i = 1;
 //對指針i解引用,能夠找存儲的第一個字節
 //若是獲得01,說明存儲的順序是01 00 00 00,則爲小端,
 //若是獲得00,說明存儲的順序是00 00 00 01,則爲大端,
 return (*(char *)&i);
}
int main()
{
 int ret = check_sys();
 if(ret == 1)//0x00 00 00 01
 {
 printf("小端\n");
 }
 else
 {
 printf("大端\n");
 }
 return 0;
 }

image-20210809164547069

固然,這裏咱們也能夠將其封裝成一個函數,根據其返回值肯定是大端仍是小端字節序。

int check_sys()
{
    int a = 1;
    char* p = (char*)&a;
    if (*p == 1)
        return 1;//小端
    else
        return 0;//大端
}

固然這個函數咱們也能夠進行簡化:

int check_sys()
{
    int a=1;
    char*p=(char*)&a;
    //返回1,小端
    //返回0,大端
    return *p;
}

也能夠再簡化:

int check_sys()
{
    int a = 1;
    return  *(char*)&a;
}
相關文章
相關標籤/搜索