✅ 2차원 배열도 동적으로 한번 만들어 보자.

2차원 배열을 malloc로 생성해 보자.

2차원 배열은 1차원 배열보다 더 복잡하다.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
	#define col_size (2) // 3x2 배열을 만들어 보자.
	#define row_size (3)	
	
/*	
	int arr[3][2] ={
		{1,2},
		{3,4},
		{5,6}
	}
	*/

	int** arr = NULL; // 2차원이므로 2중 포인터
	arr = (int**)malloc((col_size) * sizeof(int*)); // 2*4= 8바이트의 메모리 생성
	// 첫번재로 컬럼을 생성하자.
	// 주의할점: 이렇게 하면 의도와 다르지만 동작한다.
	// arr = (int**)malloc((col_size) * sizeof(int));
	// ★ 칼럼에 int*가 들어가야 하는데, int*=4바이트, int=4바이트라서 사이즈가 맞다.
	// 하지만 나중에 일차원 포인터를 연결할수는 없다.

	// 두번째로 로우를 생성하자.
	// for로 돌려도 되고
	// 포인터 연산으로 해도 된다.
	// 보기 편하라고 차례대로 나열했다.
	if (arr == NULL) { return;  }
	arr[0] = (int*)malloc(row_size * sizeof(int)); // 3 * 4 = 12 바이트 메모리 생성
	arr[1] = (int*)malloc(row_size * sizeof(int));

	for (int i = 0; i < col_size; i++) {
		for (int j = 0; j < row_size; j++) {
			//arr[i][j]= 0; // 배열을 쓰면 계속 포인터가 NULL이 아니냐고 물어본다.
			*(*(arr+i)+j) = 0; // 배열이 아니기 때문에 포인터 연산으로 해야만 한다.
			// 메모리 생성후 이렇게 초기화 하는게 번거로울때 calloc 함수를 쓰면 편리하다.
		}
	}
	printf("\\r\\n");
	
	// 이제 사용자가 원하는 값을 넣으면 된다.
	// 그런데 배열에 비해 상당히 불편하다.
	*(*(arr + 0) + 0) = 11; // arr[0][0] = 11, 의미상 이렇다는 거지
	*(*(arr + 0) + 1) = 12; // arr[0][1] = 12;
	*(*(arr + 0) + 2) = 13; // arr[0][2] = 13;

	*(*(arr + 0) + 0) = 21; // arr[1][0] = 21;
	*(*(arr + 0) + 0) = 22; // arr[1][1] = 22;
	*(*(arr + 0) + 0) = 23; // arr[1][2] = 23;

	// 제대로 들어갔는지 출력해보자.
	for (int i = 0; i < col_size; i++) {
		for (int j = 0; j < row_size; j++) {
			//printf("%d, ", arr[i][j]);
			printf("%d, ", *(*(arr + i) + j));
		}
	}

	// 해제도 꼭 해줘야 한다.
	free(arr[0]); // ★★★★★ 꼭 써주자! 잊어서는 안된다.
	free(arr[1]);	
	*(arr+0) = NULL; // 2차원 배열은 순서도 맞춰줘야 한다.
	*(arr+1) = NULL; // 개별 1차원을 free 해주고, 최종 2차원 배열의 메모리를 해제해 줘야 한다.
	free(arr); // 이 반대로 해줄경우 에러 혹은 댕글링 포인터가 된다.
	arr = NULL;

	return 0;
}