티스토리 뷰
이차원배열을 어떻게 malloc하느냐는 질문을 받았는데, 쉽지 않은 주제네요.
이차원 배열을 함수 매개변수로 넘기는 방법을 먼저 살펴보겠습니다. C 언어에서 2차원 배열은 포인터의 배열이고 각 포인터는 COLS칸 배열의 주소를 가집니다. 여기서 ROWS, COLS는 상수를 나타내고 rows, cols는 변수일 수 있습니다. (참고)
void func(int array[ROWS][COLS]) { ...
void func_vla(int rows, int cols, int array[rows][cols]) { ...
void test(int array[][COLS], int rows) { ...
void test(int (* array)[COLS], int rows) { ...
첫번째는 상수 크기의 배열을 넘기는 방법입니다. 고정크기라면 이차원 배열로 넘길 수가 있는 거죠. 그리고 크기는 부르는 쪽이나 이 함수나 다 알고 있으니 따로 알려줄 필요가 없겠죠?
두 번째는 행과 열 크기를 다 모를 때입니다. 이 때 행과 열의 크기를 먼저 주고 이차원 배열을 넘기면 받는 쪽에서 그 배열을 받을 수 있습니다. 이 때도 이차원배열의 주소를 포인터(int**)로 넘지는 것은 동일합니다. 크기를 전달해 주어서 접근할 수 있게 해주는 것이지요.
세 번째와 네 번째는 같은 표현입니다. 네 번째는 int [COLS] 배열을 가리키는 포인터 array를 받겠다는 뜻입니다. 이것은 곧 COLS개 배열이 여러 개 연속해 나올 수있는 포인터가 되겠지요? 재미있는 것은 이 변수 array를 포인터 더하기 연산을 하면, 즉 array++하면 sizeof(int)*COLS 크기만큼 주소가 증가한다는 것입니다.
이렇게 하면 rows*COLS 이차원 배열을 매개변수로 넘길 수 있습니다. 그럼 이제 이차원 배열의 malloc을 살펴볼까요? 다음 글에서 잘 정리되어 있습니다. (코드는 그대로 가져오고 내용을 번역하면서 설명과 그림을 보충했습니다)
https://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/
(1) 이차원 배열을 하나의 덩어리로 malloc하는 방법
int r = 3, c = 4;
int *arr = (int *)malloc(r * c * sizeof(int));
int i, j, count = 0;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
*(arr + i*c + j) = count++;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
printf("%d ", *(arr + i*c + j));
이 방법은 한꺼번에 r*c개 메모리를 할당받은 다음 i, j번째 항을 찾을 때 포인터의 주소 계산을 하는 방법입니다. arr + i*c + j번째가 [i][j] 번째가 되는 거죠.
사실 위 코드의 for 루프는 다음과 같이 써도 됩니다. arr은 주소를 가지는 포인터의 포인터가 됩니다.
int* p = *arr;
for (i = 0; i < r*c; i++, p++)
*p = count++;
for (i = 0, p = *arr; i < r*c; i++, p++)
printf("%d ", *p);
(2) 포인터 배열을 이용하는 방법
int r = 3, c = 4, i, j, count;
int *arr[r];
for (i = 0; i < r; i++)
arr[i] = (int *)malloc(c * sizeof(int));
// Note: arr[i][j] == *(*(arr+i)+j)
count = 0;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
arr[i][j] = count++; // 또는 *(*(arr+i)+j) = count++
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
printf("%d ", arr[i][j]);
함수에 매개변수로 보낼 때 이런 형태였죠? 포인터의 배열을 먼저 선언하고 각 포인터에 c개의 배열을 할당해 주는 방법입니다. 여기서 중요한 것은 각 행이 따로 malloc 되므로 연속된 메모리에 있다는 보장은 없다는 것입니다.
(3) 포인터의 포인터를 이용하는 방법
int r = 3, c = 4, i, j, count;
int **arr = (int **)malloc(r * sizeof(int *));
for (i=0; i<r; i++)
arr[i] = (int *)malloc(c * sizeof(int));
// Note that arr[i][j] is same as *(*(arr+i)+j)
count = 0;
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
arr[i][j] = count++; // OR *(*(arr+i)+j) = count++
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
printf("%d ", arr[i][j]);
이중포인터는 주소의 주소를 가지는 포인터입니다. 이런 그림이 되겠네요. 사용은 (2)번 방법과 동일합니다.
(4) 진짜 이차원 배열처럼 동적할당하는 방법
우리는 이차원 배열이 (2)나 (3)처럼 여러 개의 힙 조각들로 흩어져 있기 보다는 한 덩어리로 모여 있는 것을 원합니다. C에서는 이차원 배열을 그렇게 할당해 줍니다. 그럼 진짜 이차원 배열처럼 동적할당하는 방법을 살펴보겠습니다. 가장 복잡하지만 배울 것이 많은 코드입니다. 실제 C 언어에서 이차원 배열이 할당되어 있는 것과 같은 모양으로 만들어줍니다.
int r=3, c=4, len=0;
int *ptr, **arr;
int count = 0,i,j;
len = sizeof(int *) * r + sizeof(int) * c * r;
arr = (int **)malloc(len); // arr은 한 덩어리의 주소를 받아 int**로 저장했다.
// ptr는 이차원 배열의 시작 부분을 가리킨다.
ptr = (int *)(arr + r);
// arr의 행포인터들을 해당 위치를 가리키게 설정해 준다.
for(i = 0; i < r; i++)
arr[i] = (ptr + c * i);
// 이차원 배열에 차례로 증가하는 값을 넣는다.
for (i = 0; i < r; i++)
for (j = 0; j < c; j++)
arr[i][j] = count++; // OR *(*(arr+i)+j) = count++
for (i = 0; i < r; i++) {
for (j = 0; j < c; j++)
printf("%d ", arr[i][j]);
printf("\n");
}
실제 C 언어에서 이차원 배열을 할당하는 배치입니다. 제일 앞을 arr이 가리키고 있는 거구요, 앞에는 행포인터가 위치하고 있다고 보면 됩니다. 그러면 int arr[3][4];로 할당한 것과 같은 구조의 동적메모리 할당 배열이 됩니다.
'C 프로그래밍기초' 카테고리의 다른 글
C 포인터 (1) - 개요 (0) | 2020.02.11 |
---|---|
구조체란 무엇인가? (0) | 2020.02.11 |
C printf 요약매뉴얼 (0) | 2020.01.08 |
C from Python - step 1 (0) | 2019.12.25 |
- Total
- Today
- Yesterday
- Iterator
- zip
- 이터러블
- format
- python example
- 스트링 +
- 자바regex
- 동적바인딩
- comparable
- 스트링
- rust
- sort key
- contains
- contentEquals
- python exercise
- follow
- 패턴
- ToString
- Lazy evaluation
- 지연계산
- 이터레이터
- 콜렉션
- APPEND
- CompareTo
- max
- indexof
- Camel Style
- typedef
- C++ 클래스
- TypeError
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |