# Camera OV7670 第02段 const和 uint8_t、uint16_t ```javascript=6 #define UART_MODE 5 const uint8_t VERSION = 0x10; const uint8_t COMMAND_NEW_FRAME = 0x01 | VERSION; const uint8_t COMMAND_DEBUG_DATA = 0x03 | VERSION; const uint16_t UART_PIXEL_FORMAT_RGB565 = 0x01; const uint16_t UART_PIXEL_FORMAT_GRAYSCALE = 0x02; ``` ## 什麼是 const ### 解釋1 - 1:在定義變量時使用: 總結:在使用const定義變量時,一定要進行初始化操作,在操作符(*,&)左邊的修飾的是指向的內容,在右邊的是本身。 a: const int a=100; 最簡單的用法,設定說明變數a是一個常數變數。 b: int const b=100; 與a功能相同。 c: const int *a=&b; 指向常數的指針,即指針本身的值是可以改變的,但指向的內容是不能改變的。 d: int const *a=&b; 與c功能相同。 e: int * const a = &b; 常數指針,即指針本身的值是不可改變的,但指向的內容是可改變的。 f: const int * const a = &b; 指向常數的常數指針,即指針本身與指向的內容都是不可改變的。 g: const int &a=100; 常數引用,即不能改變引用的值。 - 2:在函數用使用: 總結:在函數中使用const,情況與定義變數的情況大致相同。 a: void func(const int a); 做為參數使用,說明函數內是不能修改該參數的。 b: const int func(); 做為返回值使用,說明函數的返回值是不能被修改的。 c: int func() const; 常數函數,說明函數是不能修改該類別中成員的值的,只能用於類別的成員函數中。 - 應用原則 1.先左由右,看左邊最近的是何種data type,若左邊沒有,則看右邊最近的。 2.沿著*號劃一條線, 如果const位於*的左側,則const就是用來修飾指標所指向的變數,即指標指向為常量。 如果const位於*的右側,const就是修飾指標本身,即指標本身是常量。 範本 例一: const int * Constant1 --> const int (* Constant1) 例二: int const * Constant2 --> int const (* Constant2) 例三: int * const Constant3 --> (int *) const Constant3 --> const (int *) Constant3 例四: int const * const Constant4 --> const (int *) const Constant4 解答 例一 int值不可被修改 例二 int值不可被修改 例三 指標值不可被修改 例四 int值與指標值皆不可修改 - 做個總結 記憶體的位置可以變動,但是記憶體的內容不能變動 const Parent* pParent1 = new SubA(); 記憶體的位置可以變動,但是記憶體的內容不能變動 Parent const *pParent2 = new SubA(); 記憶體的內容可以變動,但是記憶體的位址不能變動 Parent* const pParent3 = new SubA(); 記憶體的位址與內容都不能變動 const Parent* const pParent4 = new SubA(); 記憶體的位置可以變動,但是記憶體的內容不能變動 const Parent const *pParent5 = new SubA(); (參考網址{https://www.xuehua.tw/a/5ec8178c600636e92ea18dd5}) ### 解釋2 很難對它下一個標準的定義,因為的用法很靈活,似乎對它定義後總無法讓人能夠明白它的意思 例如,它有定義:一個能夠讓變數變成無法修改的常量的關鍵字。 那麼,這樣的話,就可能讓人誤解為只要有const在定義變數裡面,那變數就無論怎樣都無法修改。 這樣的理解是很片面的(下面用法方面將對這問題做探討)。 1. 為了防止傳遞的函數參數不被修改,在調用函數的形參中用const關鍵字 ```javascript= int FindNum(const int array[], int num, int conut);//聲明函數 //code... int FindNum(const int array[], int num, int count){ int i; int flag = 1; for (i = 0; (i < count) && flag; i++){ if (array[i] == num){ flag = 0; break; } } return flag; } //code... ``` 上面這例子中,編譯器會把array[ ]當作常量資料的陣列看待。所以,假如你不小心給陣列賦值,那麼,編譯器就會報錯了。 因此,當你不需要也不想修改陣列的資料時,最好用const把陣列定義為常量陣列。 2. const可以用來創建陣列常量、指標常量、指向常量的指標等 ```javascript= const char ch = 'a'; const int a[5] = {1, 2, 3, 4, 5}; const int *p = a; //a是一個陣列的首位址.p是指向常量的指標 int * const p = a; //a是一個陣列的首位址.p是指針常量; const int * const p = a; //a是一個陣列的首位址。p是指向常量的指標常量 ``` 3. 前兩種情況很簡單,現在著重分析一下後三種用法,因為這情況容易出錯 - 乾脆不用const. ```javascript= const int *p = a; ``` - p是**指向常量**的指標,因此,不可以通過給**指標賦值**來改變陣列中的資料,例如: ```javascript= const int *p = a; *p = 10; /*錯誤*/ *(p + 2) = 1; /*錯誤*/ ``` 假如指向常量指標可以改變值,那麼,就等於也改變了陣列的數。 假如你不理解,就想你和一個人綁在一起,有可能你移動了位置而他不跟著你移動嗎! - 看這運算式,const的位置和第一個不同吧!他們的用法和作用就完全不一樣了。這時候p是指標常量 ```javascript= int * const p = a; ``` - 我們知道,指標是指向了一個陣列的首位址,那麼,它的位置就不可以改變了。 - 但是你現在應該和第一個運算式比較了,現在的陣列並不是常量陣列,所以陣列的資料是可以改變的 - 而指標這時候它是不可以移動的,指向陣列第一個資料,所以它可以而且只可以改變陣列第一個 數據的值。 - 這一點請別誤解,指標常量只是它的位址不可以改變,並不是它指向的內容一定不可以改變,這一點切記! - 好啦。假如你又不理解,又有一個比較形象的例子來說明: 假如有一個固定的人拉著另外一個人的手, 注意,固定的人相當於他是不可以由其他人來替換的。 但是他可以拉其他人的手啊, 並不一定規定他必須拉同一個人的手啊。 現在你應該可以有個比較深的印象和理解吧 下面舉幾個例子幫助理解: ```javascript= int * const p = a; *p = 2; /*可以*/ *(p+1) = 10; /*可以*/ p++; /*不可以*/ ``` - 假如前面兩種運算式的本質你理解了,這種運算式你來理解根本沒有問題,const現在有兩個,而且一個const的位置是第一種情況的位置,第二個const是第二種情況的位置,所以這運算式的功能就是前兩種情況的作用總合。 ```javascript= const int * const p = a; ``` 下面舉幾個例子幫助理解: ```javascript= const int * const p = a *p = 2; /*不可以*/ *(p + 2) = 10; /*不可以*/ p++; /*不可以*/ ``` 4. const並不會阻止參數的修改 之所以把這作為一點來談,就是因為有一些朋友可能會以為在函數參數中用了const就一定不可以改變參數,這實際上是錯誤的理解,因為,它並不阻止參數的修改,下面舉個簡單的例子來闡述一下; ```javascript= #include<stdio.h> #include<ctype.h> void ChangeStr(const char *String); int main(void){ char str[] = "The C programme"; Change(str); printf(str); system("Pause"); return 0; } void ChangeStr(const char *String){ char *Source = (char *)String; while (*Source){ *Source = toupper(*Source); Source++; } } //end ``` 上面的程式把字串中的每個字元都轉換成大寫字母了。因為String把地址給了Source,而Source的值的改變編譯器並不干涉,可能有的編譯器會發出警告之類。上面的程式只是為了說明const並不會阻止參數的修改,如果象上面程式那樣,個人感覺沒什麼意義,只會讓人容易混亂而已。 [參考網站](//https://www.xuehua.tw/a/5ec870da5da483795bb685f0) ## 什麼是uint8_t、uint16_t 1. 資料型態有分signed跟unsigned,用來表示正整數或者負數使用,C如果加上uint就代表是無號數(無符號數)的意思 2. 那後面的_t又是什麼意思呢? 代表的就是他是透過typedef而來, 3. typedef又是什麼? 就是有點像是幫資料型態另外定義命名,譬如你有一些使用上的習慣,你可以透過typedef來重新命名 而通常是透過這樣命名的 ```javascript= typedef unsigned char uint8_t; typedef unsigned short int uint16_t; ``` 可以看到就是unsigned char 的型態,char又等於 1byte ,用bit來表示就是8bits 4. 無符號數是什麼?(unsigned)是計算機編程中的一種數值資料型別。 有符號數(signed)可以表示特定類型規定範圍內的整數(包括負數),而無符號數只能表示非負數(0及正數)。 有符號數能夠表示負數的代價是能夠表示的正數範圍的縮小,因為其約一半的數值範圍要用來表示負數 (如8位有符號整數中,對應8位無符號整數表示128~255的部分被用於表示-127~ -1)。無符號數可以利用其所占有的所有位來表示較大的數。 例如,16位有符號整數可表示 -32768~32767 之間的任意整數,而16位無符號整數可表示 0~65535 之間的數。若將有符號數轉換為二進位,則其數值類型允許的最左一位用於表示符號(1為負數,0為正數和0),但在無符號數中,最左一位與其右各位一樣用於表示數值。 參考網站(https://jefflin1982.medium.com/%E8%8F%9C%E9%B3%A5c-%E4%BB%80%E9%BA%BC%E6%98%AFunit8-t-99ce61277945)