# [C語言] 指標、陣列與結構 相較於`基本型別`int, double,C語言還有`衍伸資料型別(Derived datatype)`。 其中很常被應用的,就是`指標(pointer)`、`陣列(Array)`、`結構(struct)` ## 指標(Pointer) 指標的設計,是為了讓CPU可以**間接取得**資料,有以下優點: - 可動態配置/釋放記憶體 - 不同的函式可共享大量的儲存空間 - 改善程式存取資料的效率 #### 指標的特性 指標本身是一個物件,指向任何一個**已存在物件**,可以被指定或拷貝 - 指標宣告時,就要定義是指向哪種資料型態的物件 - 指標本身儲存的值,有三種情況 - 指向一個物件,值裡面存放另一個物件的位址 - 指向另一個物件尾端後的位置 - 沒有指向任何物件,值即為null ``` C int i = 42; int *p = &i; //p指向i ``` ``` C= #include <stdio.h> int main() { int i = 42; int *p; //* 跟在一個型別,且是宣告的一部份,所以p是一個pointer p = &i; //p指向i printf("i: %d\n", i); printf("*p: %d\n", *p); printf("p: %p\n", p); printf("&i: %p\n", &i); return 0; } ``` ## 陣列(Array) ### 陣列的定義 是一個複合資料型別,放置**單一型別**物件的容器,並且以位置來存取它們,有固定的尺寸。 通常是存放`char`, `int`型別的資料 ### 字元陣列的特性 在C語言,一個字串本身會在結尾自帶`\0`結束字元,所以當我們把一個字串用陣列儲存時,實際結尾會有一個`\0`也被儲存。 例如: `char str[20] = "Hello"` 在陣列的存放,是一個長度為6的陣列,最後擺放`\0` ### 宣告與初始化陣列 - 靜態宣告 ```c int a[10]; //宣告一個10個int物件的陣列 int *p[10]; //宣告一個10個int指標的陣列 ``` 因為陣列的長度要在編譯期就決定好。如果想要在執行期動態生成陣列,要用動態配置記憶體的方式。 - 動態宣告 我們同樣用 malloc() 函式來配置記憶體。參考以下敘述: ```c= int *arr = (int *) malloc(sz * sizeof(int)); ``` 我們以 sizeof 求得單一元素的大小後,乘上陣列的長度 sz 即可配置一塊足夠大小的記憶體,用來儲存陣列 arr 的元素。由此可知,陣列在電腦中以是一整塊連續的記憶體來儲存,所以可以用索引值快速存取。 如果想要在配置記憶體時一併將元素初始化為 0,改用 calloc() 函式即可。但 calloc() 函式的參數略有不同: ``` int *arr = (int *) calloc(sz, sizeof(int)); ``` 由於多了初始化的動作,calloc() 函式會比 malloc() 函式慢一點點。 使用完後同樣要釋放記憶體: ```c= free(arr); ``` 由於陣列內部是單一且連續的記憶體區塊,所以可在單一 free() 函式呼叫中釋放掉。不論使用 malloc() 或 calloc(),皆使用 free() 來釋放記憶體。 我們來看一個動態配置記憶體陣列的範例: ```c= #include <stdio.h> #include <stdlib.h> int main(void) { int size = 0; printf("輸入長度:"); scanf("%d", &size); int *arr = malloc(size * sizeof(int)); printf("指定元素:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = ", i); scanf("%d" , arr + i); } printf("顯示元素:\n"); for(int i = 0; i < size; i++) { printf("arr[%d] = %d\n", i, *(arr+i)); } free(arr); return 0; } ``` ### 存取陣列元素 陣列使用零或正整數存取陣列元素。參考以下範例: ```clike= #include <stdio.h> int main(void) { int arr[] = {3, 4, 5}; printf("%d", arr[0]); printf("%d", arr[1]); printf("%d", arr[2]); return 0; } ``` ### 指標算術 對一個指標做加減,會移動指向陣列中的位置 ``` C++ #include <iostream> using namespace std; int main() { int a[] = {1,2,3,4,5}; int *ip = a; //等同於int *ip = &a[0] int *ip2 = ip + 4; //ip2指向a[4] cout << ip2 << endl; return 0; } ``` ### 走訪陣列 走訪陣列元素的方式是使用 for 迴圈搭配計數器 (counter)。參考下例: ```clike= #include <stdio.h> int main(void) { int arr[] = {3, 4, 5, 6, 7}; for (int i = 0; i < 5; i++) { printf("%d\n", arr[i]); } return 0; } ``` ### 計算陣列大小 C 陣列本身沒有儲存陣列大小的資訊。如果想要知道陣列的大小,得自行計算。參考下例: ```clike= #include <stdio.h> int main(void) { int arr[] = {3, 4, 5, 6, 7}; int sz = sizeof(arr) / sizeof(int); printf("%d", sz); return 0; } ``` 在此範例程式中,我們在第 6 行分別計算陣列大小和陣列元素大小,將其相除後即可得陣列長度。在本例中其值為 5。 但這個方式只對自動配置記憶體的陣列有效,若陣列使用動態配置記憶體,則無法使用這個方法。 ### 多維陣列 先前的範例皆為一維陣列,但 C 語言允許多維陣列。參考以下宣告多維陣列的敘述: ```clike= int mtx[3][2] = { {1, 2}, {3, 4}, {5, 6} }; ``` 我們同樣可以對多維陣列存取索引值: ```clike= #include <stdio.h> int main(void) { int mtx[3][2] = { {1, 2}, {3, 4}, {5, 6} }; printf("%d\n", mtx[1][1]); return 0; } ``` ## 結構(struct) ### 宣告struct `結構(struct)`可以讓使用者創造自己定義的資料型別 例如一本書的銷售資訊包含多種資料: ``` C= struct Sales_data { char* book_no; int units_sold; int price; double revenue; }; ``` 其中`book_no`, `units_sold `, `price`,`revenue`是這個struct的data members,簡稱members ### 呼叫struct ``` c= #include <stdio.h> struct Sales_data { char* book_no; int units_sold; int price; double revenue; }; int main() { struct Sales_data book1; book1.book_no = "AAA"; book1.units_sold = 1000; book1.price = 200; book1.revenue = book1.units_sold * book1.price; printf("Revenue: %.2f\n", book1.revenue); return 0; } ``` ###### tags: `C/C++程式語言觀念`