# 程式設計
## CH12. 動態記憶體配置
### 老師:王銘宏
---
## 本章目錄
1. 動態記憶體配置
2. 一維動態陣列
3. 雙重指標與二維動態陣列
4. 字串與記憶體操作
---
## 動態記憶體配置
----
### 為什麼動態記憶體配置?
以前我們寫的程式,都是先將變數宣告好,在開始執行時系統配置記憶體空間給這些變數,並在程式結束後釋放記憶體空間。
----
而如果有以下需求,則要使用「動態記憶體配置」:
* 宣告陣列的時候還不確定所需陣列大小。
* 希望不再使用變數時,釋放記憶體空間。
* 希望能寫一個能產生新的陣列的副程式,且該陣列不會隨著副程式結束而釋放。
----
### 動態記憶體配置函式
\<stdlib.h>中提供了動態記憶體相關函式
* `void *malloc(size_t size)`
* (memory allocation) 配置指定大小的一段記憶體空間,並傳回空間的起始位址 (配置的空間未初始化)。
* `void free(void *ptr)`
* 釋放動態記憶體空間。
----
以下程式碼,malloc配置了一段記憶體,起始位址為007C5B78,將此位址轉型為int指標並存入指標變數ptr。

----
如果欲配置的記憶體空間太大而無法配置,malloc會回傳NULL。

---
## 一維動態陣列
----
### 使用malloc配置動態陣列
使用malloc配置的記憶體空間並不會初始化為0。

----
### 動態陣列配置函式
* `void *calloc(size_t nitems, size_t size)`
* (contiguous allocation) 配置陣列用的記憶體空間,並傳回空間的起始位址,配置的空間會被初始化為0。
----
使用calloc配置的記憶體空間會初始化為0。

----
### 改變動態配置記憶體空間大小
* `void *realloc(void *ptr, size_t size)`
* 改變已配置的動態記憶體空間的大小為size,並回傳空間的起始位址,新配置的空間未初始化。
----
使用realloc新配置的空間不會初始化。

----
若原本配置的位置的空位不足以配置新的指定大小,則會重新配置到新的位址。

----
### 函式回傳動態陣列

---
## 雙重指標與二維動態陣列
----
雙重指標 (pointer to pointer) 型態所儲存的值為指標的位址,可以說是指向指標的指標。

----
### 二維動態陣列
上一章([第9.2頁](/@yorutsuki123/109-ch11#/8/1))我們使用指標陣列來儲存許多靜態陣列的位址,來達成二維陣列的效果,二維動態陣列則是將上一章的靜態陣列部分全部替換成動態陣列。
----
建立一個3\*5的動態二維陣列:

----
### 練習
----
使用二維動態陣列計算矩陣乘法,實作以下副程式:
```c
int** createMatrix(int x, int y);
int** multiMatrix(int** a, int** b, int x, int y, int z);
void printMatrix(int** a, int x, int y);
void freeMatrix(int** a, int x);
```
createMatrix會讓使用者輸入矩陣內容並建立二維動態陣列,multiMatrix會使兩個矩陣相乘並回傳一個新的二維動態陣列,printMatrix會將矩陣內容印出,freeMatrix會將二維動態陣列釋放。
----
主程式:
```c
int main() {
int x, y, z, **m1, **m2, **m3;
scanf("%d%d%d", &x, &y, &z);
m1 = createMatrix(x, y);
m2 = createMatrix(y, z);
m3 = multiMatrix(m1, m2, x, y, z);
printMatrix(m3, x, z);
freeMatrix(m1, x);
freeMatrix(m2, y);
freeMatrix(m3, x);
}
```
----
範例輸入:
```
2 3 4
3 5 4
1 2 3
2 0 3 6
1 2 3 2
4 2 3 0
```
範例輸出:
```
27 18 36 28
16 10 18 10
```
---
## 字串與記憶體操作
----
在第9章時,我們介紹了一些<string.h>提供的函式:
* 計算字串長度:strlen
* 複製字串:strcpy、strncpy
* 連接字串:strcat、strncat
* 比較字串:strcmp、strncmp
----
在本節,我們來認識更多<string.h>提供的函式:
* 搜尋相關:
* 搜尋字元:strchr、strrchr
* 搜尋字串:strstr
* 搜尋字元集:strpbrk、strspn、strcspn
* 切割字串:strtok
* 記憶體函式:
* memcpy、memmove、memcmp、memchr、memset
----
### 搜尋字元:strchr、strrchr
```c
char * strchr ( const char * str, int character );
char * strrchr ( const char * str, int character );
```
函式strchr尋找字串中某個字元第一次出現的位置,若沒找到則回傳NULL。
函式strrchr會尋找字串中某字元最後一次出現的位置,若沒找到則回傳NULL。
----
尋找字串中第一個與最後一個出現的字元'a':

----
### 搜尋字串:strstr
```c
char * strstr ( char * str1, const char * str2 );
```
函式strstr會搜尋它的第一個字串引數,找出第二個字串引數第一次出現的位置。
----
尋找字串中第一個出現的字串"ate":

----
### 搜尋字元集:strpbrk
```c
char * strpbrk ( char * str1, const char * str2 );
```
函式strpbrk會尋找第一個字串中第一次出現屬於第二個字串之字元的位置。
----
使用迴圈呼叫strpbrk找出母音:

----
### 搜尋字元集:strspn、strcspn
```c
size_t strspn ( const char * str1, const char * str2 );
size_t strcspn ( const char * str1, const char * str2 );
```
函式strspn會判斷第一個字串從頭開始到第一個不屬於第二個字串的字元為止,共有多少個字元。此函式會傳回這一段字串的長度。
函式strcspn會判斷第一個引數的字串從頭開始一直到第一個屬於第二個字串的字元為止,共有多少個字元,並傳回這一段字串的長度。
----
使用strspn與strcspn抓取字串出現的第一串數字:

----
### 切割字串:strtok
```c
char * strtok ( char * str, const char * delimiters );
```
函式strtok用來將字串切成數個字符 (token)。字符是由分界字元 (delimiter) 所分隔出的一連串字元。
----
將句子的每個單字分割出來:

----
### 複製記憶體區塊:memcpy
```c
void * memcpy ( void * destination, const void * source,
size_t num );
```
函式memcpy會從第二個引數所指向的物件中複製某個指定數目的字元到第一個引數所指向的物件。此函式的指標引數可以指向任何資料型別的物件。memcpy函式會將物件的位元組當做字元來處理。
----
使用memcpy複製陣列內容

----
### 複製記憶體區塊:memmove
```c
void * memmove ( void * destination, const void * source,
size_t num );
```
函式memmove會從第二個引數所指向的物件中複製某個指定數目的字元到第一個引數所指向的物件。複製動作會先複製到一個暫時的陣列,然後再由這個暫時的陣列複製到第一個物件。
----
memmove與memcpy和strncpy最大的不同是memmove可以將複製的來源區塊與貼上的目標區塊重疊。

----
### 比較記憶體區塊:memcmp
```c
int memcmp ( const void * ptr1, const void * ptr2,
size_t num );
```
函式memcmp會比較第一個和第二個引數的某個指定個數的字元。此函式的指標引數可以指向任何資料型別的物件。memcmp函式會將物件的位元組當做字元來處理。
----
使用memcmp比較陣列內容

----
### 搜尋位元組:memchr
```c
void * memchr ( const void * ptr, int value, size_t num );
```
函式memchr會在某物件的指定個數的字元內,尋找某個位元組 (unsigned char) 第一次出現的位置。如果找到的話,memchr會傳回指向此位元組的指標,否則便會傳回NULL指標。
----
尋找字串中第一個出現的字元'u':

----
### 填充位元組:memset
```c
void * memset ( void * ptr, int value, size_t num );
```
函式memset會將第二個引數當作unsigned char複製到第一個引數所指向之位址之後指定大小內的所有位元組。
memset常用在將陣列或動態記憶體歸零。
----
使用memset將動態陣列歸零。

---
END
~部分參考C程式設計藝術第七版~
{"metaMigratedAt":"2023-06-15T19:56:34.947Z","metaMigratedFrom":"YAML","title":"12.動態記憶體配置","breaks":true,"slideOptions":"{\"transition\":\"slide\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"2bee7838-fa3e-44cc-825a-9f9f045138f1\",\"add\":5449,\"del\":159}]"}