최근에 연구실에서 OpenCL 관련 일을 하고 있는데 CUDA나 OpenCL 내부의 C 언어는 다차원 배열 혹은 다중 포인터를 쓸 수 없더군요.
그래서 단일 포인터(* 연산자를 하나만 쓰는 포인터)를 적당히 잘 다뤄서 1차원 배열을 2차원 혹은 3차원 배열처럼 이용해야 했습니다.
어차피 다차원 배열도 코드 측면에서 다차원으로 보일 뿐이지 메모리에는 1차원 배열처럼 쭈루룩 들어갑니다. 다만 arr[2][3] 이렇게 직관적으로 접근이 가능할 뿐이죠.
아래 코드는 이해를 하기 위해 연습한 코드입니다.
main.h
1 2 3 4 5 6 |
#include <iostream> using std::cout; using std::endl; #define at2(arr, x, y, width) arr[x * width + y] #define at3(arr, x, y, z, depth, width) arr[x * depth * width + y * depth + z] |
헤더에 정의한 두 #define은 [킹갓선배님]의 조언으로 만든 [매크로 함수]입니다. 각자 2차원, 3차원 배열에 접근하는 코드를 함수의 형태로 간단하게 표현할 수 있게 해줍니다.
2차원 배열을 행렬로 간주했을때 A<i,j>에 접근하는 코드는 A[i * 열의 크기 + j] 이며, for문을 이용해 순차적으로 접근하는 코드는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 |
for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { //left[i * width + j] = i + j; at2(left, i, j, width) = i + j; //right[i * width + j] = -1 * (i + j) + j; at2(right, i, j, width) = -1 * (i + j) - 1 * (1 + j); } } |
for문은 height(이미지에선 높이, 행렬에선 행의 크기) 먼저 시작하고 그 안에 width(이미지에선 너비, 행렬에선 열의 크기) for문이 들어있습니다. 그래야 행 단위로 반복이 되거든요.
i에 width를 곱하는 행동이 곧 특정 행의 첫 번째 열에 접근하는 것과 같습니다. 그런 다음 j를 더하면 원하는 열에 접근하게 되지요.
마찬가지로 3차원 배열을 행렬로 간주했을때 A<i,j,k>에 접근하는 코드는 A[i * 깊이의 크기(z축에 해당) * 열의 크기 + j * 열의 크기 + k] 이며, for문을 이용해 순차적으로 접근하는 코드는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { printf("At (%d, %d, ?)\t", i, j); for (int k = 0; k < disprange; k++) { //datacost[i * disprange * width + j * disprange + k] at3(datacost, i, j, k, disprange, width) = at2(left, i, j, width) + at2(right, i, j, width); cout << at3(datacost, i, j, k, disprange, width); } cout << endl; } } |
아래는 위에서 설명한 코드의 완전한 부분입니다.
main.cpp
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#include "main.h" int main() { int width = 3; int height = 2; int disprange = 4; double * left = new double[width * height]; double * right = new double[width * height]; double * datacost = new double[width * height * disprange]; std::fill(left, left + width * height - 1, 0); std::fill(right, right + width * height - 1, 0); std::fill(datacost, datacost + width * height * disprange - 1, 0); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { //left[i * width + j] = i + j; at2(left, i, j, width) = i + j; //right[i * width + j] = -1 * (i + j) + j; at2(right, i, j, width) = -1 * (i + j) - 1 * (1 + j); } } cout << R"([Insertion of "datacost"])" << endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { printf("At (%d, %d, ?)\t", i, j); for (int k = 0; k < disprange; k++) { //datacost[i * disprange * width + j * disprange + k] at3(datacost, i, j, k, disprange, width) = at2(left, i, j, width) + at2(right, i, j, width); cout << at3(datacost, i, j, k, disprange, width); } cout << endl; } } cout << R"([2D Array "left"])" << endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { cout << at2(left, i, j, width); } cout << endl; } cout << R"([2D Array "right"])" << endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { cout << at2(right, i, j, width); } cout << endl; } cout << R"([3D Array "datacost"])" << endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { for (int k = 0; k < disprange; k++) { cout << at3(datacost, i, j, k, disprange, width); } cout << " "; } cout << endl; } delete[] left; delete[] right; delete[] datacost; return 0; } |
컴파일해서 돌리면 아래와 같이 나오게 됩니다. 3차원 배열은 깊이 축에 해당하는 원소들을 공백없이 붙여서 표현했습니다.
사실 C와 C++에서는 배열과 메모리와 포인터의 개념이 섞여있어 단순히 수학에서의 행렬 개념에 직관적으로 대응하지 못하는 경우가 많습니다.
하지만 역으로 생각해서 이 언어로 다차원 행렬을 잘 구현할 줄 알면 배열과 메모리, 포인터 개념을 깊이 이해했다는 뜻이니 열심히 공부합시다.
http://cin.fail/38383d0/
를 참고하면 2차원 표현과 3차원 표현을 하나의 함수로 처리할 수 있지 않을까 ㅎㅎ (WIDTH, HEIGHT, DEPTH 가 글로벌 상수라는 가정 하에)
그리고 나 선배 아니다 13 동기다 후후