当前位置:网站首页>數據的存儲(進階)

數據的存儲(進階)

2022-06-22 05:37:00 頭發沒有代碼多

目錄

數據類型介紹

 類型的基本歸類: 

整形家族:

 浮點型家族

構造類型: 

指針類型 

空類型: 

整形在內存中的存儲 

原碼、反碼、補碼 

原反補轉換法則

大小端介紹  

什麼大端小端: 

 習題:如何判斷當前機器是大端還是小端存儲

 練習題

習題2

習題3 

 習題4  

習題5  unsigned三碼習題

習題6 signed char取值範圍問題

習題7  無符號char取值範圍習題

習題8 關於strlen返回值類型的習題

浮點型在內存中的存儲 

 浮點數存儲規則

浮點數如何保存 

 指數E的存放規則

 浮點數如何往出拿

E不全為0或不全為1

存放E的空間全為0  

 存放E的空間全為1

​編輯 浮點數列題


數據類型介紹

char         //字符數據類型
short       //短整型
int         //整形
long         //長整型
long long   //更長的整形
float       //單精度浮點數
double       //雙精度浮點數

 類型的意義:

1. 使用這個類型開辟內存空間的大小(大小决定了使用範圍)。
2. 如何看待內存空間的視角

 類型的基本歸類: 

整形家族:

char            //char'類型的本質是ASCII碼值,所以在整形裏面
unsigned char
signed char
short
unsigned short [int]
signed short [int]
int
unsigned int
signed int
long
unsigned long [int]
signed long [int]

 一般情况下int a,此時a是signed int類型,而char不一樣char a,a此時是signed char還是unsigned char這個C語言標准是未定義的,由編譯器决定char是signed還是unsigned

 浮點型家族

float
double
浮點型家族:只要是錶示小數就可以使用浮點型,flaot 的精度低,存儲的數值範圍較小,double的精度高,存儲的數據的範圍更大。

構造類型: 

> 數組類型
> 結構體類型 struct
> 枚舉類型 enum
> 聯合類型 union

指針類型 

int *pi;
char *pc;
float* pf;
void* pv;

空類型: 

void 錶示空類型(無類型)
通常應用於函數的返回類型、函數的參數、指針類型。

 

紅色框錶示函數不需要返回值,藍色框錶示函數不需要參數,但如果我們硬給函數傳參,函數會正常運行,只不過是沒有接收參數罷了 ,倆個void其實都課省略,但最好不要進行省略

整形在內存中的存儲 

int a = 20;
int b = -10;

 我們知道為 a 分配四個字節的空間。 那如何存儲? 下來了解下面的概念:

原碼、反碼、補碼 

計算機中的整數有三種2進制錶示方法,即原碼、反碼和補碼。
三種錶示方法均有符號比特數值比特兩部分,符號比特都是用0錶示,用1錶示,而數值比特
正數的原、反、補碼都相同。
負整數的三種錶示方法各不相同。

原碼

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

反碼 

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

補碼 

反碼+1就得到補碼。 在計算機中整形數據以補碼的形式進行存儲

前面的博客有涉及到原碼反碼補碼的一些知識和二進制下的計算

點這裏跳轉

在計算機系統中,數值一律用補碼來錶示和存儲。原因在於,使用補碼,可以將符號比特和數值域統
一處理;
同時,加法和减法也可以統一處理(CPU只有加法器)此外,補碼與原碼相互轉換,其運算過程
是相同的,不需要額外的硬件電路。

原反補轉換法則

方法一

一、正整數的原碼、反碼、補碼完全一樣,即符號比特固定為0,數值比特相同

二、負整數的符號比特固定為1,由原碼變為補碼時,規則如下:

      1、原碼符號比特1不變,整數的每一比特二進制數比特求反,得到反碼

      2、反碼符號比特1不變,反碼數值比特最低比特加1,得到補碼

       補碼得原碼:補碼-1得反碼,反碼符號比特不變其餘比特取反得原碼

方法二

 一、正整數的原碼、反碼、補碼完全一樣,即符號比特固定為0,數值比特相同

補碼得原碼:補碼符號比特不變其餘比特按比特取反,取反之後得結果+1得到原碼

-1和1以補碼的形式進行相加, 加完之後為33比特,多出一比特。然後去掉最高比特就得到最終結果的補碼

 

大小端介紹  

什麼大端小端: 

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

  

 我們在編譯器中運行可發現數據是反著存的,為什麼要倒著放呢?

 這裏如11 22 33 44,其實這些數據在內存中是可以隨便放的,但是使用的時候必須得原樣拿出,如數據本來是11 22 33 44,存放形式 33 22 11 44,但是使用的時候必須按11 22 33 44使用,即拿出來的時候必須得恢複原樣,但是亂序存儲過於麻煩,一般選擇正序存儲或倒序存儲

我們把這倆中方式分別稱為大端字節序存儲和小端字節序存儲

 大端字節序存儲:

把一個數據的高比特字節序的內容存放在低地址處,把低比特字節序的內容放在高地址處,就是大端字節序存儲。

小端字節序存儲:

把一個數據的低比特字節序的內容存放在低地址處,把高比特字節序的內容放在高地址處,就是小端字節序存儲。

一個字節看不出大小端的順序

為什麼有大端和小端: 

為什麼會有大小端模式之分呢?這是因為在計算機系統中,我們是以字節為單比特的,每個地址單元 都對應著一個字節,一個字節為8 bit。但是在C語言中除了8 bit的char之外,還有16 bit的short 型,32 bit的long型(要看具體的編譯器),另外,對於比特數大於8比特的處理器,例如16比特或者32 比特的處理器,由於寄存器寬度大於一個字節,那麼必然存在著一個如何將多個字節安排的問題。因 此就導致了大端存儲模式和小端存儲模式。
例如:一個 16bit 的 short 型 x ,在內存中的地址為 0x0010 , x 的值為 0x1122 ,那麼 0x11 為 高字節, 0x22 為低字節。對於大端模式,就將 0x11 放在低地址中,即 0x0010 中, 0x22 放在高 地址中,即 0x0011 中。小端模式,剛好相反。我們常用的 X86 結構是小端模式,而 KEIL C51 則 為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。

 簡單來說:一個數據有若幹個字節,至於如何在內存中存放,就產生了大小端問題

 習題:如何判斷當前機器是大端還是小端存儲

#include<stdio.h>
int check(int a)
{
	return *(char*)&a;
}
int main()
{
	int a = 1;
	check(a);
	if (check)
		printf("小端存儲\n");
	else
		printf("大端存儲\n");
	return 0;
}

 

 練習題

#include <stdio.h>
int main()
{
    char a= -1;
    signed char b=-1;
    unsigned char c=-1;
    printf("a=%d,b=%d,c=%d",a,b,c);
    return 0; 
}

 有符號的char取值範圍-128-127,

其它數字均可按照原碼,反碼,補碼轉換規則,算出來,但是10000000不能這樣算,直接人為是-128 

unsigned char的取值範圍是0-255,應為無符號數沒有負數

 

 

 short類型占倆個字節,也就是16個二進制數字,取值範圍在下面

 

 

 這裏的C為什麼是255?看下面圖解

 

 

習題2

#include <stdio.h>
int main()
{
    char a = -128;
    printf("%u\n",a);
    return 0; 
}

 

習題3 

#include <stdio.h>
int main()
{
	char a = 128;
	printf("%u\n", a);
	printf("%d\n", a);
	return 0;
}

這個跟前面的題一樣都是先發生截斷,然後整形提昇,然後再打印

 習題4  

#include <stdio.h>
int main()
{
	int i = -20;
	unsigned int j = 10;
	printf("%d\n", i + j);
	return 0;
}

習題5  unsigned三碼習題

#include<stdio.h>
int main()
{
	unsigned int i;
	for (i = 9; i >= 0; i--)
	{
		printf("%u\n", i);
	}
	return 0;
}

 

 我們看到這裏9-0都是正常打印但是到-1就出現了問題,具體原因請看下圖

 

 

二進制下的32個1,換成十進制為一個特別大的數字,所以會打印4294967295,之後每次i--這個數都會减一 

習題6 signed char取值範圍問題

#include<stdio.h>
#include<string.h>
int main()
{
		char a[1000];
		int i;
		for (i = 0; i < 1000; i++)
		{
			a[i] = -1 - i;
		}
		printf("%d", strlen(a));
	return 0;
}

 這裏把數字放入到數組a中,而a是char類型,範圍為-128~127

 這裏是-1-i,其實就是上面這個圖逆序的走法,從-1~-128~127~0

\0的ASCII碼值是0,strlen遇到\0會停下來,所以在遇到0之前,數組裏面已經存了128+127個數字了,所以打印255

習題7  無符號char取值範圍習題

#include<stdio.h>
unsigned char i = 0;
int main()
{
	for (i = 0; i <= 255; i++)
	{
		printf("hello world\n");
	}
	return 0;
}

 程序運行後會進入死循環,這是因為i是unsigned char類型範圍是0~255,前面有說過,i為255之後,i+1,此時i又變為0,陷入死循環

習題8 關於strlen返回值類型的習題

#include<stdio.h>
#include<string.h>
int main()
{
	if (strlen("abc") - strlen("abcdef") >= 0)
		printf(">");
	else
		printf("<");
	return 0;
}

這裏會打印>是因為strlen返回值為unsigned int類型,無論怎樣運算結果都>=0.

可點開這個鏈接,最後面有一道類似的題

浮點型在內存中的存儲 

浮點數家族包括: floatdoublelong double 類型。
浮點數錶示的範圍:float.h中定義
用everything查找float.h然後用VS打開,就可看到浮點數的範圍,整形輸入litmits.h可查看範圍

 

 浮點數存儲規則

根據國際標准IEEE(電氣和電子工程協會) 754,任意一個二進制浮點數V可以錶示成下面的形式:
(-1)^S * M * 2^E
(-1)^S錶示符號比特,當S=0V為正數;當S=1V為負數。
M錶示有效數字,大於等於1,小於2
2^E錶示指數比特。

 舉例來說:

十進制的5.0,寫成二進制是 101.0 ,相當於 1.01×2^2
那麼,按照上面V的格式,可以得出S=0M=1.01E=2
十進制的-5.0,寫成二進制是 -101.0 ,相當於 -1.01×2^2 。那麼,S=1M=1.01E=2
9.5f

 9.5f其實用二進制錶示為:1001.1,這跟權重有關系,因為2^-1為0.5

1001.1=1.0011*2^3

S=0,M=1.0011,e=3

例:9.6f

二進制:1001.XXXXXX,因為這個0.6不好錶示,需要一堆0和1,但是如果數字過多就有可能存不下,因此浮點數就產生了精度問題

float 4byte double 8byte

浮點數如何保存 

IEEE 754規定:
對於32比特的浮點數,最高的1比特是符號比特s,接著的8比特是指數E,剩下的23比特為有效數字M

 

對於64比特的浮點數,最高的1比特是符號比特S,接著的11比特是指數E,剩下的52比特為有效數字M

 

 特別規定:

前面說過, 1≤M<2 ,也就是說,M可以寫成 1.xxxxxx 的形式,其中xxxxxx錶示小數部分。
IEEE 754規定,在計算機內部保存M時,默認這個數的第一比特總是1,因此可以被舍去,只保存後面的 xxxxxx部分。
比如保存1.01的時 候,只保存01,等到讀取的時候,再把第一比特的1加上去。
這樣做的目的,是節省1比特有效數字。以32比特 浮點數為例,留給M只有23比特, 將第一比特的1舍去以後,等於可以保存24比特有效數字。

 指數E的存放規則

首先,E為一個無符號整數(unsigned int),這意味著,如果E8比特,它的取值範圍為0~255;如果E11比特,它的取值範圍為0~2047
如0.5=1.0*2^-1 M=1.0,E=-1,S=0
E為負數不在範圍之內,所以 IEEE 754規定,存入內存時E的真實值必須再加上一個中間數:
                           對於8比特的E(float),這個中間數是127。
                           對於11比特的E(double),這個中間數是1023
剛才E=-1,按float類型,E+127=126,把126存放到E所在的空間當中(8個bit)

 

 

 結果一致,為小端存儲

 浮點數如何往出拿

E不全為0或不全為1

這時,浮點數就采用下面的規則錶示,即指數E的計算值减去127(或1023),得到真實值,再將
有效數字M前加上第一比特的1
比如:
0.51/2)的二進制形式為0.1,由於規定正數部分必須為1,即將小數點右移1比特,則為
1.0*2^(-1),其階碼為-1+127=126,錶示為
01111110,而尾數1.0去掉整數部分為0,補齊023比特00000000000000000000000,則其二進
制錶示形式為:
0 01111110 00000000000000000000000

 

存放E的空間全為0  

這時,浮點數的指數E等於1-127(或者1-1023)即為真實值,
有效數字M不再加上第一比特的1,而是還原為0.xxxxxx的小數。這樣做是為了錶示±0,以及接近於 0的很小的數字。

 存放E的空間全為1

 這時,如果有效數字M全為0,錶示±無窮大(正負取决於符號比特s);

 浮點數列題

#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;
}

 

 所以會打印0.0000000000

 

原网站

版权声明
本文为[頭發沒有代碼多]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/173/202206220529193093.html