Quick Syntax
int matrix[rows][cols]는 행 우선으로 이어진 배열의 배열입니다. 함수는 행 하나의 폭을 알아야 하므로 열 크기를 매개변수 선언에 유지합니다.
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
void clear_matrix(size_t rows, size_t cols, int matrix[rows][cols]) {
for (size_t r = 0; r < rows; r++) {
for (size_t c = 0; c < cols; c++) matrix[r][c] = 0;
}
}
int main(void) {
enum { ROWS = 2, COLS = 3 };
int matrix[ROWS][COLS] = {{1, 2, 3}, {4, 5, 6}};
clear_matrix(ROWS, COLS, matrix);
size_t rows = ROWS;
size_t cols = COLS;
if (cols != 0 && rows > SIZE_MAX / cols) return 1;
size_t count = rows * cols;
if (count > SIZE_MAX / sizeof(int)) return 1;
int *flat = malloc(count * sizeof *flat);
if (flat == NULL) return 1;
size_t r = 1;
size_t c = 2;
flat[r * cols + c] = 1; /* flat buffer의 행 우선 인덱스 */
free(flat);
return 0;
}연속 2D 배열은 int **가 아닙니다. 한 번 할당한 flat buffer는 한 번 free하고, 행을 각각 할당한 int **만 각 행과 포인터 배열을 따로 해제합니다.
문법
C의 2차원 배열은 "배열의 배열"이고 메모리는 행 우선으로 이어집니다. 그래서 메모리 관점에서는 1 2 3 4 5 6이 한 줄로 붙어 있는 것과 가깝습니다.
m[r][c]를 주소로 계산하려면 한 행의 길이를 알아야 하므로, 두 번째 차원 크기가 중요해집니다.
순회와 전달
함수 인자에서는 보통 이렇게 갑니다.
void print_matrix(int rows, int cols, int m[rows][cols]);위 VLA parameter 표기는 컴파일러와 빌드 옵션이 VLA를 지원할 때의 형태입니다. 이식성 제약이 있거나 행·열 크기를 명시적으로 전달하고 싶다면 flat buffer를 data[r * cols + c]로 인덱싱하는 방식도 선택할 수 있습니다.
열이 고정이면 아래처럼 둘 수도 있습니다.
void print_4col(int rows, int m[][4]);순회는 행을 바깥, 열을 안쪽에 두는 편이 메모리 배치와 잘 맞습니다.
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
printf("%d ", m[r][c]);
}
}동적 2D는 한 번에 rows * cols 원소를 할당하는 flat buffer와, 행마다 따로 할당하는 int **가 다릅니다. flat buffer는 한 번 free하면 되지만 곱셈 overflow를 검사해야 하고, 행별 할당은 실패한 행을 역순으로 정리하며 각 행과 포인터 배열을 모두 free해야 합니다.
빠른 점검
int a[R][C]는 행 우선으로 이어진 배열 블록입니다.- 함수 인자에서 다음 차원 크기는 주소 계산에 필요합니다.
- 행을 바깥, 열을 안쪽에 두는 순회가 메모리 배치와 잘 맞습니다.
- 동적 2D는
int **와 flat buffer가 모양은 비슷해도 메모리 구조가 다릅니다. - 정적 2D 배열과
int **를 같은 타입처럼 섞으면 안 됩니다.
주의할 점
int **와 int [R][C]는 같은 것이 아닙니다. 둘 다 "2차원처럼" 보여도 메모리 구조와 인덱싱 전제가 다르므로, 정적 2D 배열을 받는 함수에 int **를 넘길 수 있다고 생각하면 타입/메모리 해석 문제가 바로 생깁니다.
참고 링크
2 sources