跳到正文

Wiki

資料型別與資料存放

約 12 分鐘閱讀

本文由簡體中文內容確定性轉換,並受版本化術語表保護。

1.資料型別

1.1.整型

作用:整型變量表示的是==整數型別==的資料

C++中能夠表示整型的型別有以下幾種方式,區別在於所佔記憶體空間不同

資料型別 佔用空間 取值範圍
short(短整型) 2位元組 (-2^15 ~ 2^15-1)
int(整型) 4位元組 (-2^31 ~ 2^31-1)
long(長整形) Linux 64位通常為8位元組,32位環境通常為4位元組 由平臺位數決定
long long(長長整形) 8位元組 (-2^63 ~ 2^63-1)

1.2.sizeof關鍵字

**作用:**利用sizeof關鍵字可以==統計資料型別所佔記憶體大小==

語法: sizeof( 数据类型 / 变量)

示例:

int main() {
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。

	cout << "short 类型所占内存空间为: " << sizeof(short) << endl;

	cout << "int 类型所占内存空间为: " << sizeof(int) << endl;

	cout << "long 类型所占内存空间为: " << sizeof(long) << endl;

	cout << "long long 类型所占内存空间为: " << sizeof(long long) << endl;


	// 返回 0 表示程序正常结束。
	return 0;
}

執行結果(型別大小或型別名可能隨編譯器和平臺變化):

short 类型所占内存空间为: 2
int 类型所占内存空间为: 4
long 类型所占内存空间为: 8
long long 类型所占内存空间为: 8

整型結論:==short < int <= long <= long long==

1.3.實型(浮點型)

作用:用於==表示小數==

浮點型變數分為兩種:

  1. 單精度float
  2. 雙精度double

兩者的區別在於表示的有效數字範圍不同。

資料型別 佔用空間 有效數字範圍
float 4位元組 7位有效數字
double 8位元組 15~16位有效數字

示例:

int main() {

	float f1 = 3.14f;
	double d1 = 3.14;

	cout << f1 << endl;
	cout << d1<< endl;

	cout << "float  sizeof = " << sizeof(f1) << endl;
	cout << "double sizeof = " << sizeof(d1) << endl;

	//科学计数法
	float f2 = 3e2; // 3 * 10 ^ 2 
	cout << "f2 = " << f2 << endl;

	float f3 = 3e-2;  // 3 * 0.1 ^ 2
	cout << "f3 = " << f3 << endl;


	return 0;
}

執行結果(型別大小或型別名可能隨編譯器和平臺變化):

3.14
3.14
float  sizeof = 4
double sizeof = 8
f2 = 300
f3 = 0.03

1.4.字元型

**作用:**字元型變數用於顯示單個字元

語法:char ch = 'a';

注意1:在顯示字元型變數時,用單引號將字元括起來,不要用雙引號

注意2:單引號內只能有一個字元,不可以是字串

  • C和C++中字元型變數只佔用==1個位元組==。
  • 字元型變數並不是把字元本身放到記憶體中儲存,而是將對應的ASCII編碼放入到儲存單元

示例:

int main() {
	
	char ch = 'a';
	cout << ch << endl;
	cout << sizeof(char) << endl;

	//ch = "abcde"; //错误,不可以用双引号
	//ch = 'abcde'; //错误,单引号内只能引用一个字符

	cout << (int)ch << endl;  //查看字符a对应的ASCII码
	ch = 97; //可以直接用ASCII给字符型变量赋值
	cout << ch << endl;


	return 0;
}

執行結果(型別大小或型別名可能隨編譯器和平臺變化):

a
1
97
a

ASCII碼錶格:

ASCII 控制字元 ASCII 字元 ASCII 字元 ASCII 字元
0 NUT 32 (space) 64 @ 96
1 SOH 33 ! 65 A 97 a
2 STX 34 " 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 , 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DCI 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 TB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 / 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 `
31 US 63 ? 95 _ 127 DEL

ASCII 碼大致由以下兩部分組成:

  • ASCII 非列印控制字元: ASCII 表上的數字 0-31 分配給了控制字元,用於控制像印表機等一些外圍裝置。
  • ASCII 列印字元:數字 32-126 分配給了能在鍵盤上找到的字元,當檢視或列印文件時就會出現。

1.5.跳脫字元

**作用:**用於表示一些==不能顯示出來的ASCII字元==

現階段我們常用的跳脫字元有: \n \\ \t

跳脫字元 含義 ASCII碼值(十進位制)
\a 警報 007
\b 退格(BS) ,將當前位置移到前一列 008
\f 換頁(FF),將當前位置移到下頁開頭 012
\n 換行(LF) ,將當前位置移到下一行開頭 010
\r 回車(CR) ,將當前位置移到本行開頭 013
\t 水平製表(HT) (跳到下一個TAB位置) 009
\v 垂直製表(VT) 011
\\ 代表一個反斜線字元"" 092
' 代表一個單引號(撇號)字元 039
" 代表一個雙引號字元 034
? 代表一個問號 063
\0 數字0 000
\ddd 8進位制跳脫字元,d範圍0~7 3位8進位制
\xhh 16進位制跳脫字元,h範圍09,af,A~F 3位16進位制

示例:

int main() {
	// 程序从 main 函数开始执行,下面的语句会按顺序运行。
	
	
	cout << "\\" << endl;
	cout << "\tHello" << endl;
	cout << "\n" << endl;


	// 返回 0 表示程序正常结束。
	return 0;
}

執行結果:

\
	Hello

1.6.字串型

作用:用於表示一串字元

兩種風格

  1. C風格字串char 变量名[] = "字符串值"

    示例:

    int main() {
    
    	char str1[] = "hello world";
    	cout << str1 << endl;
        
    
    	return 0;
    }

注意:C風格的字串要用雙引號括起來

  1. C++風格字串string 变量名 = "字符串值"

    示例:

    int main() {
    
    	string str = "hello world";
    	cout << str << endl;
    	
    
    	return 0;
    }

注意:C++風格字串,需要加入標頭檔案==#include==

1.7.布林型別 bool

**作用:**布林資料型別代表真或假的值

bool型別只有兩個值:

  • true --- 真(本質是1)
  • false --- 假(本質是0)

bool型別佔==1個位元組==大小

示例:

int main() {

	bool flag = true;
	cout << flag << endl; // 1

	flag = false;
	cout << flag << endl; // 0

	cout << "size of bool = " << sizeof(bool) << endl; //1
	

	return 0;
}

執行結果(型別大小或型別名可能隨編譯器和平臺變化):

1
0
size of bool = 1

1.8.類型別名 typedef

C/C++ 提供了 typedef 關鍵字,您可以使用它來為型別取一個新的名字。下面的例項為單位元組數字定義了一個術語 BYTE

typedef unsigned char byte;
typedef unsigned char uint8_t;
typedef float fp32;
typedef double fp64;

執行結果: 此程式碼塊不含 main 函式,是語法、介面或分檔案示例,不能獨立執行,因此沒有終端輸出。

1.9.資料單位、二進位制與補碼

  1. 資料單位及其轉換

    1. 1位元組byte = 8位元bit = 8位二進位制

    2. 資料是以 二進位制 的形式存在電腦記憶體中的

    3. 進位制轉換

      1. 在C語言中,0b開頭的資料代表2進位制,0x開頭的數代表16進位制。因為2進位制較長,所以我們在程式碼中,一般把2進位制轉化成16進位制進行表示。
    4. 資料的原碼

      1. 無符號(unsigned)的資料型別: 無符號資料所佔的所有2進位制都表示資料的大小,假設佔8位2進位制,那麼該數最大是255(其對應的2進位制:1111 1111),最小是0(對應2進位制是0000 0000)。

      2. 有符號(signed)的資料型別:有符號的資料其最高位二進位制是符號位,符號位是0則是正數,符號位是1則為負數。假設佔8位2進位制,去掉1位2進位制,還剩7位2進位制來表示資料的大小。那麼其最大是127(其2進位制是0111 1111),最小是-128(其2進位制是1000 0000,-127的是1111 1111),這個-128其實是用了-0的二進位制表示,因為咱們有0(0000 0000)了,所以-0(理論是1000 0000)就沒有啥意義了,所以我們規定把-0的二進位制表示成-128。

    5. 資料的命名 :

      1. 由於unsigned char,int等資料型別名稱無法突出 資料的二進位制 有無符號,佔多少二進位制,所以我們用typedef給其起新的別名(比如 新別名 uint8_t,int32_t)

        1. uint8_t 中的u代表unsigned無符號,8代表佔8位二進位制,也就是1個位元組。

        2. int32_t前面不帶u,所以是有符號的,佔32位二進位制,所以也就是佔4個位元組,所以是int型別的別名

        3. 因為char 佔 1 位元組,8 位元,8 位二進位制,而且有符號,所以我們叫他int8_t

        4. 因為unsigned short int 無符號,佔 2位元組,16位元,16位2進位制,所以叫uint16_t,其他的類似。

        5. float佔4位元組,32位二進位制,所以叫fp32

        6. double佔8位元組,64位二進位制,所以叫fp64

      2. 具體的程式碼(在C++中自帶一部分別名,可以不寫一部分別名,但建議寫):

        1. typedef unsigned char uint8_t;

        2. typedef short int int16_t;

        3. typedef unsigned int uint32_t;

        4. typedef float fp32;

  2. 資料解析:

    1. 應用場景介紹:一般感測器會一直給我們的微控制器、工控機發送資料,比如說傳送我們機器人所走的路程(假設單位是毫米,是個短整型數)。

    2. 資料處理目標: 比如獲取路程(短整型,2個位元組,16位2進位制)

    3. 感測器傳資料: 一般的通訊來說,感測器會一個位元組一個位元組的給微控制器和工控機不斷髮資料,一般一個佔n位元組的物理量資料,要分成n個位元組,也就是n個變數來發,又因為資料以2進位制的形式儲存,一般先發最高的8位二進位制。

    4. 微控制器、工控機接受到的資料: 比如獲取機器人的路程,該資料佔2位元組,所以感測器要分成兩個變數來發送。先接收到的變數我們叫DH(資料的高8位2進位制),後接收到的資料我們叫DL(資料的低8位2進位制)。

    5. 資料處理思路: 我們現在擁有的是兩個普通的8位二進位制變數(uint8_t型別的),我們想要獲得帶符號的16位2進位制的資料,機器人的路程(int16_t型別的)。

      1. 位操作 : 假如說,DH是0x9D(對應2進位制是1001 1101),DL是0x57(對應2進位制是0101 0111)。那麼我們想要的16位資料就是把DH當16位中的最高的8位,DL當最低的8位,也就是我們想要的資料DATA(其二進位制是 1001 1101 0101 0111)。我們想實現這個效果,就需要位操作,把DH向左移8位,讓他變成1001 1101 0000 0000,然後再把左移後的DH 與 DL進行按位或運算,也就是1001 1101 0000 0000 與 0000 0000 0101 0111進行按位或,最後就得出來DATA是1001 1101 0101 0111了。

      2. 強轉型別: 我們上面已經得出來data的2進位制了,再進行強轉化成有符號的數,也就是把最高位中的1變成符號位,具體程式碼是int16_t DATA = (int16_t) ((uint16_t)DH << 8 | DL ),這樣就處理成功了。

  3. 資料在記憶體中的二進位制表示方式

    1. 計算機儲存資料的前置知識

      1. 計算機底層儲存資料時使用的是二進位制數字,但是計算機在儲存一個數字時並不是直接儲存該數字對應的二進位制數字,而是儲存該數字對應二進位制數字的 補碼

      2. 機器碼:一個數在計算機的儲存形式是二進位制數,我們稱這些二進位制數為機器數,機器數是有符號,在計算機中用機器數的次最高位的左側存放符號位,0表示正數,1表示負數

      3. 真值:因為機器數帶有符號位,所以機器數的形式值不等於其真實表示的值(真值),以機器數1000 0001為例,其真正表示的值(首位為符號位)為-1,而形式值(首位就是代表1)為129; 因此將帶符號的機器數的真正表示的值稱為機器數的真值

    2. 原碼、反碼、補碼:

      1. 原碼 的表示與機器數真值表示的一樣,即用第一位表示符號,其餘位表示數值。也就是

          正數:就是它對應的二進位制數。 負數:將絕對值對應的二進位制最左邊位變為1。

      【+1】= 原:[ 0000 0001 ]
      【-1】= 原:[ 1000 0001 ]
      1. 反碼:

          正數 : 和原碼相同。 負數 : 在其原碼的基礎上,符號位不變,其餘各位取反。

      【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ]
      【-1】= 原: [ 1000 0001 ] = 反:[ 1111 1110 ]
      1. 補碼

          正數 : 補碼是其原碼本身。 負數 : 補碼是在其原碼的基礎上,符號位不變,其餘各位取反後加1(即在反碼的基礎上加1)

      【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ] = 补:[ 0000 0001 ]
      【-1】= 原: [ 1000 0001 ] = 反:[ 1111 1110 ] = 补:[ 1111 1111 ]
    3. 資料在計算機中的儲存形式

      1. 計算機實際只儲存補碼,所以原碼轉換為補碼的過程,也可以理解為資料儲存到計算機記憶體中的過程:

      1. 正數:在原、反、補碼中,正數的表示是一模一樣的

      2. 負數:負數的表示是不相同的,所以對於負數的補碼來說,我們是不能直接用進位制轉換將其轉換為十進位制數值的,因為這樣是得不到計算機真正儲存的十進位制數的,所以應該將其轉換為原碼後,再將轉換得到的原碼進行進位制轉換為十進位制數(機器數包含符號位)

      3. 為什麼要誕生原碼、反碼和補碼?

        1. 原因:對於只有正數的加減運算來說,用原碼計算是沒有任何問題的,但是當既有正數,也有負數的時候,計算機無法判斷最高位是否是符號位。
    4. 總結:

      1. 二進位制的最高位是符號位:0表示正數,1表示負數

      2. 正數的原碼反碼補碼都一樣,三碼合一。

      3. 負數

        1. 負數的反碼 = 它的原碼符號位不變,其它位取反。

        2. 負數的補碼 = 它的反碼 + 1, 負數的反碼 = 負數的補碼 - 1 。

      4. 0 的反碼、補碼都是 0 。

      5. 在計算機運算的時候都是以 “補碼” 的方式來運算的。

      6. 運算結果是以 原碼 形式展現的。