跳到正文

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. 運算結果是以 原碼 形式展現的。