###### tags: `cpp` # malloc在dynamic 2d array dynamic產生2d array,為什麼不能使用memset來設定初始值 ```cpp int N = 3, M=5; // row, col int **table = (int **)malloc(sizeof(int *) * N); memset(table, 0, N * M * sizeof(int)); ``` ::: danger **segmentation fault** ::: ## fixed array 先看一般的固定陣列 ```cpp int arr[3][5]; memset(arr, 0, 3 * 5 * sizeof(int)); printf("arr [ptr]: value\n"); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) printf("[%d]: %d\t", &arr[i][j], arr[i][j]); printf("\n"); } ``` ``` output: arr [ptr]: value [6421920]: 0 [6421924]: 0 [6421928]: 0 [6421932]: 0 [6421936]: 0 [6421940]: 0 [6421944]: 0 [6421948]: 0 [6421952]: 0 [6421956]: 0 [6421960]: 0 [6421964]: 0 [6421968]: 0 [6421972]: 0 [6421976]: 0 ``` arr是在程式執行時就被配置在heap/stack記憶體中,所以可以看到記憶體空間是連續的,每4Byte`(int)`為一格 而memset的作用則是為一段連續空間賦值,所以能讓`fixed array`透過`memset`的方式填0 ## 回到dynamic `malloc`作用是請求一段連續的空間,並回傳ptr位置,但並不保證每次malloc都會是接續上次的位置,詳細看附圖與code ![img](https://i.imgur.com/wOT5Lcd.png) ```cpp int N = 3, M = 5; int **table = (int **)malloc(sizeof(int *) * N); for (int i = 0; i < N; i++) table[i] = (int *)malloc(sizeof(int) * M); for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) table[i][j] = 0; printf("table [ptr]: value\n"); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) printf("[%d]: %d\t", &table[i][j], table[i][j]); printf("\n"); } ``` ``` output: table [ptr]: value [11081184]: 0 [11081188]: 0 [11081192]: 0 [11081196]: 0 [11081200]: 0 [11081264]: 0 [11081268]: 0 [11081272]: 0 [11081276]: 0 [11081280]: 0 [11081344]: 0 [11081348]: 0 [11081352]: 0 [11081356]: 0 [11081360]: 0 ``` 可以看到`table[0][4]`最後一個元素的位置是[11081200],但是下一個元素`table[1][0]`元素卻跳到[11081264] ::: danger 在這種非連續配置的空間,使用memset就有可能不小心覆蓋到table[i]的pointer,導致 **segmentation fault** ::: > 我認為這error是這樣跳出的,但沒有很確定,不過方向應該是對的 像是這段code,嘗試存取非法空間,也會導致一樣的問題 ```cpp int *qwe = (int *)10; printf("%d\n", *qwe); // Exception has occurred. Segmentation fault ``` ## 所以該如何配置dynamic 2d array 最普通的方法 ```cpp int N = 3, M = 5; int **table = (int **)malloc(sizeof(int *) * N); for (int i = 0; i < N; i++) table[i] = (int *)malloc(sizeof(int) * M); for (int i = 0; i < N; i++) for (int j = 0; j < M; j++) table[i][j] = 0; ``` 透過[calloc][calloc]來分配,會在分配時值就初始為0 ```cpp int N = 3, M = 5; int **table = (int **)malloc(sizeof(int *) * N); for (int i = 0; i < N; i++) table[i] = (int *)calloc(M, sizeof(int)); ``` 如果要用`memset`的話,就是要分別對每一個table[i]指標做`memset` ```cpp int N = 3, M = 5; int **table = (int **)malloc(sizeof(int *) * N); for (int i = 0; i < N; i++) table[i] = (int *)malloc(sizeof(int) * N); for (int i = 0; i < N; i++) memset(table[i], 0, M * sizeof(int)); ``` ## 為甚麼memset對int賦值時只能0或-1 因為[memset][memset],是以char(1byte)為單位,一個一個byte去賦值 ```cppp void * memset ( void * ptr, int value, size_t num ); ``` > Value to be set. The value is passed as an int, but the function fills the block of memory using the unsigned char conversion of this value. 如果想賦值為10,可能會想這樣寫,但結果並不是這樣 ```cpp int test[5]; memset(test, 10, 5 * sizeof(int)); for (int i = 0; i < 5; i++) { printf("%d\n", test[i]); } ``` ``` output: 168430090 168430090 168430090 168430090 168430090 ``` ## 為什麼會這樣? 前面提到是以byte為單位賦值,而int為32bit(4byte),賦值時其實是這樣的 ``` dec:10 = bin:1010 00001010 00001010 00001010 00001010 ``` 這串二進制轉成10進制,10 << 24 | 10 << 16 | 10 << 8 | 10,就是**168430090** 所以想把動態int陣列初始化為任意number的話,只能用迴圈 畢竟memset原本的用處就是拿來產生n個char,所以這func才會在string.h中 ## 補充: [Getting value from a dynamic allocated 2d array by pointers](https://stackoverflow.com/questions/8122320/getting-value-from-a-dynamic-allocated-2d-array-by-pointers) [Is the memory allocation done by malloc always contiguous?](https://stackoverflow.com/questions/66739598/is-the-memory-allocation-done-by-malloc-always-contiguous) [Does malloc() allocate a contiguous block of memory?](https://stackoverflow.com/questions/625270/does-malloc-allocate-a-contiguous-block-of-memory) [calloc]: https://www.cplusplus.com/reference/cstdlib/calloc/ [memset]: https://www.cplusplus.com/reference/cstring/memset/