# 重拾 C 語言::指標的部分 ## 指標 [C 語言規格書](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf)中有針對指標語法做出明確定義。然而,仍有部分是規格書中沒有交代清楚的,像是: - 以實作定義 - 未指定 - 未定義 > Jserv 說過: 閱讀規格書可以大幅省去臆測。 > 更詳細的資訊可以參考[你所不知道的 C 語言: 開發工具和規格標準](https://hackmd.io/@sysprog/c-standards?type=view)。 ### 指標的用途 - 建立快速有效率的程式碼 - 提供更方便解決問題的方法 - 動態記憶體配置 - 簡化表達式 簡潔表達式是為了解決問題,而非為了簡潔而簡潔,像是: ```c char *name[] = {"Miller", "Jones", "Anderson"}; printf("%c\n", *(*(name+1)+2)); printf("%c\n", names[1][2]); ``` 根據上述程式碼,即使利用指標來表示更為精簡,但是卻降低了可讀性。 在這個情況下,使用 `printf("%c\n", names[1][2]);` 會是更好的選擇。 - 利用指標傳遞結構資料,避免傳遞大量資料造成的負擔 - 保護以參數傳入參數的資料 ### 指標宣告 宣告整數和整數指標: ```c= int num; int *pi; ``` > 補充: 整數指標 > 整數指標是指該指標所指向的記憶體位址是用來存放整數的。 #### 星號的位置 以下定義都有相同的效果: ```c= int* p; int * p; int *p; int*p; ``` #### 動動腦 根據下列程式碼,請問 `value` 和 `ptr` 的型態: ```c= int* ptr, value; ``` > 答: > ptr 為 int pointer , > value 為 integer 。 #### 閱讀宣告 ```c= pci; //pci 是個變數 *pci; //pci 是個指標變數 int *pci; //pci 是個指向整數的指標變數 const int *pci; //pci 是個指向常數整數的指標變數 ``` ### 位址運算子 位址運算子 `&` 會回傳運算元的位址,像是: ```c= int num = 0; int *pi = # ``` 上述程式碼代表: `num` 變數的數值設為0, `pi` 指向 `num` 的位址。 > 補充: `&` 並不是 `AND` ,它代表 `Address of` 唷。 ### NULL 的概念 當 NULL 被指派給指標時表示指標沒有指向任何東西, `null` 的概念表示指標持有一個不會與其他指標相等的特殊數值。 NULL 巨集是個轉型為 void 指標的整數常數0,在許多函數中都這樣定義: ```c= #define NULL ((void*)0) ``` ### void 指標 void 指標可以讓使用者自由指定任何資料型別: ```c= void *pv; ``` 特性: - void 指標與 char 指標有相同的表示方式與記憶體對齊。 - void 指標永遠不會與其他指標相等。不過,兩個指標指派為 NULL 的 void 指標會相等。 void 指標可以讓我們的 C 程式碼更具彈性,它可以存放任何數值,當我們需要使用時再做轉型即可: ```c= int num; int *pi = # void *pv = pi; pv = (int*) pv; printf("Value of pi: %p\n", pi) ``` ### 全域與靜態指標 當指標宣告為全域或是靜態時,會在程式起始時初始化為 NULL : ```c= int *globalpi; void foo(){ static int *staticpi; // ... } int main(){ // ... } ``` 根據上述範例,在初始化時這兩個指標皆為 NULL 。 ## LP64 Data models 即使 C 語言標準並沒有明確規範,考慮到可攜性以及相容性,資料指標的大小幾乎相等。不過,函數指標就可能與資料指標不同。 | C Data Type | LP64 | ILP64 | LLP64 | ILP32 | LP32 | | -------- | -------- | -------- | -------- | -------- | -------- | | char | 8 | 8 | 8 | 8 | 8 | | short | 16 | 16 | 16 | 16 | 16 | | _int32 | | 32 | | | | | int | 32 | 64 | 32 | 32 | 32 | | long | 64 | 64 | 32 | 32 | 32 | | long long | | | 64 | | | | pointer | 64 | 64 | 64 | 32 | 32 | > 補充: > 實際使用的模型通常可以透過編譯器參數控制。 ### 內建指標型別 處理指標時經常使用到四種內建指標型別: - size_t - ptrdiff_t - intptr_t - uintptr_t #### 認識 size_t `size_t` 表示 C 語言中所有物件的最大尺寸,因為對大小而言負數沒有意義是個無號整數。這個型別為系統表達可定址記憶體空間大小時提供可攜性的方法, `size_t` 類型是 `sizeof` 運算子的傳回值類型,也是許多其他函數參使用的類型,包含了 `malloc` 與 `strlen` 等等。 我們可以在多個標準頭檔找到 `size_t` 的宣告: ```c= #ifndef __SIZE_T #define __SIZE_T typedef unsigned int size_t; #endif ``` 此外, `size_t` 型別的數值是無號數,使用錯誤的欄位格式可能會造成錯誤的結果。 (建議使用 `%zu` ,在不支援時,可以考慮使用 `%u` 或是 `%zu` 。) ```c= size_t sizet = -5; printf("%d\n", sizet); printf("%zu\n", sizet); ``` 這樣子做會得到以下輸出: ``` -5 4294967291 ``` ### 使用 sizeof 與指標 `sizeof` 運算子可用於取得指標的大小,以下程式會印出 `char` 指標的大小: ```c= printf("Size of *char: %d\n", sizeof(*char)); ``` > 補充: > 對於 C 語言來說, `sizeof` 是 operator 唷!