프로그래머스 입문 120842 2차원으로 만들기
문제
int** solution(int num_list[], size_t num_list_len, int n);
정수 배열 num_list를 앞에서부터 n개씩 끊어 2차원 배열로 만드시오.
num_list_len은 항상 n의 배수이므로, n개씩 나누어도 남는 원소가 없다.
| num_list | n | result |
|---|---|---|
| [1, 2, 3, 4, 5, 6, 7, 8] | 2 | [[1, 2], [3, 4], [5, 6], [7, 8]] |
| [100, 95, 2, 4, 5, 6, 18, 33, 948] | 3 | [[100, 95, 2], [4, 5, 6], [18, 33, 948]] |
풀이
문제 이름은 2차원으로 만들기이고 반환형은 int**다.
C에서 2차원 데이터를 표현할 때는 원소를 메모리에 연속해서 배치하는지에 따라 두 방식으로 나눌 수 있다.
첫 번째는 모든 원소를 하나의 연속된 메모리 블록에 저장하는 방식이다.
행과 열의 모양으로 접근하지만, 메모리에서는 1차원 배열처럼 원소가 차례로 놓인다.
result
|
v
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 4 | 5 | 6 |
+---+---+---+
| 7 | 8 | 9 |
+---+---+---+
memory
|
v
+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
두 번째는 행마다 별도의 1차원 배열을 할당하고, 행의 주소를 포인터 배열에 저장하는 방식이다.
각 행은 서로 다른 메모리 블록에 있으므로 원소 전체가 연속해서 놓이지 않는다.
result
|
v
+----+ +---+---+
| *--+----> | 1 | 2 |
+----+ +---+---+
+----+ +---+---+---+
| *--+----> | 3 | 4 | 5 |
+----+ +---+---+---+
+----+ +---+---+---+---+
| *--+----> | 6 | 7 | 8 | 9 |
+----+ +---+---+---+---+
연속 메모리 할당
#include <stdlib.h>
#include <string.h>
int** solution(int num_list[], size_t num_list_len, int n)
{
int col_count = n;
int row_count = num_list_len / col_count;
int** result = malloc(sizeof(int*) * row_count);
int* buffer = malloc(sizeof(int) * num_list_len);
memcpy(buffer, num_list, sizeof(int) * num_list_len);
for (int row = 0; row < row_count; row++)
{
result[row] = buffer + row * col_count;
}
return result;
}
num_list의 값을 하나의 연속된 buffer에 깊은 복사(deep copy)하고,
result의 각 행 포인터에는 buffer에서 col_count만큼 떨어진 주소를 저장한다.
그러면 실제 원소는 1차원으로 배치되어 있어도, 호출자는 result[row][col]처럼 2차원 배열과 같은 방식으로 접근할 수 있다.
result
+------------+
0 | buffer + 0 | ----+
+------------+ |
1 | buffer + 3 | ----|-----------+
+------------+ | |
2 | buffer + 6 | ----|-----------|-----------+
+------------+ | | |
v v v
buffer +---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+---+
0 1 2 3 4 5 6 7 8
^ ^ ^
| | |
result[0] result[1] result[2]
예를 들어 result[1]에는 buffer + 3이 저장되어 있다.
따라서 result[1][0]은 buffer[3]과 같고, 값은 4가 된다.
printf("%d\n", result[0][2]); // 3
printf("%d\n", result[1][0]); // 4
printf("%d\n", result[1][1]); // 5
printf("%d\n", *(result[1] + 1)); // 5
비연속 메모리 할당
#include <stdlib.h>
int** solution(int num_list[], size_t num_list_len, int n)
{
int col_count = n;
int row_count = num_list_len / col_count;
int** result = malloc(sizeof(int*) * row_count);
for (int row = 0; row < row_count; row++)
{
result[row] = malloc(sizeof(int) * col_count);
for (int col = 0; col < col_count; col++)
{
result[row][col] = num_list[row * col_count + col];
}
}
return result;
}
가독성을 위해 매개변수 n을 col_count에 담아 열 개수라는 의미를 드러냈다.
col_count는 한 행에 들어갈 원소 수이고, row_count는 전체 길이를 열 개수로 나눠 구한 행 개수다.
이 코드에서 주의할 점은 result와 result[row]의 자료형과 역할을 구분하는 것이다.
result는 int**로, 각 행을 가리키는 int*의 주소를 담는다.
반면 result[row]는 int*로, 해당 행의 int 원소를 저장할 공간을 가리킨다.