Try   HackMD
tags: cpp

malloc在dynamic 2d array

dynamic產生2d array,為什麼不能使用memset來設定初始值

int N = 3, M=5; // row, col
int **table = (int **)malloc(sizeof(int *) * N);

memset(table, 0, N * M * sizeof(int));

segmentation fault

fixed array

先看一般的固定陣列

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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]

在這種非連續配置的空間,使用memset就有可能不小心覆蓋到table[i]的pointer,導致
segmentation fault

我認為這error是這樣跳出的,但沒有很確定,不過方向應該是對的

像是這段code,嘗試存取非法空間,也會導致一樣的問題

int *qwe = (int *)10;
printf("%d\n", *qwe);
// Exception has occurred. Segmentation fault

所以該如何配置dynamic 2d array

最普通的方法

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來分配,會在分配時值就初始為0

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

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,是以char(1byte)為單位,一個一個byte去賦值

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,可能會想這樣寫,但結果並不是這樣

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

Is the memory allocation done by malloc always contiguous?

Does malloc() allocate a contiguous block of memory?