산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> C 언어

C 언어 포인터와 다차원 배열 마스터하기

C 언어에서 배열은 연속된 메모리 위치에 저장된 동일 타입 값들의 모음입니다. 1차원이든 다차원이든 배열의 각 요소는 하나 이상의 고유한 정수 인덱스로 식별됩니다.

반면 포인터는 변수 주소를 저장합니다. 배열에서 0번째 요소 주소가 배열의 포인터입니다. 역참조 연산자를 사용해 포인터가 가리키는 값에 접근할 수 있습니다.

C에서 1차원, 2차원, 다차원 배열을 선언할 수 있습니다. 차원(dimension)이란 컬렉션에서 요소를 식별하는 데 필요한 인덱스 수를 말합니다.

포인터와 1차원 배열

1차원 배열에서 각 요소는 단일 정수로 식별됩니다.

int a[5] = {1, 2, 3, 4, 5};

여기서 숫자 "1"은 0번 인덱스, "2"는 인덱스 1, 이런 식입니다.

0번째 요소 주소를 저장하는 변수가 그 포인터입니다.

int *x = &a[0];

단순히 배열 이름도 0번째 요소 주소를 가리킵니다. 따라서 다음 표현식도 가능합니다.

int *x = a;

예제

포인터 값이 데이터 타입 크기만큼 증가하므로 x++는 포인터를 배열 다음 요소로 이동시킵니다.

#include <stdio.h>
int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int length = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    int *ptr = arr;
    while (i < length) {
        printf("arr[%d]: %d \n", i, *(ptr + i));
        i++;
    }
    return 0;
}

출력

arr[0]: 1
arr[1]: 2
arr[2]: 3
arr[3]: 4
arr[4]: 5

포인터와 2차원 배열

1차원 배열이 요소 리스트라면, 2차원 배열은 표나 행렬 같습니다.

2차원 배열 요소들은 논리적으로 행과 열로 배치됩니다. 따라서 요소 위치는 행 번호와 열 번호 두 인덱스로 결정됩니다. 행과 열 인덱스 모두 0부터 시작.

int arr[2][2];

이런 배열은 다음과 같이 표현됩니다.

Col0 Col1 Col2
Row0 arr[0][0] arr[0][1] arr[0][2]
Row1 arr[1][0] arr[1][1] arr[1][2]
Row2 arr[2][0] arr[2][1] arr[2][2]

표 형태 배열은 논리적 표현일 뿐입니다. 컴파일러는 연속된 바이트 블록을 할당합니다. C에서 배열 할당은 행 우선(row-major) 방식으로, 요소들이 행 단위로 읽혀 들어갑니다.

여기서 3행 4열 2차원 배열(첫 대괄호 숫자는 항상 행 수)을 선언합니다.

int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

컴파일러는 위 2차원 배열 메모리를 행 우선 순서로 할당합니다. 첫 요소 주소가 1000이고 int 크기가 4바이트라 가정하면, 배열 요소들 메모리 위치는 다음과 같습니다.

Row 0 Row 1 Row 2
값 1 2 3 4 5 6 7 8 9 10 11 12
주소 1000 1004 1008 1012 1016 1020 1024 1028 1032 1036 1040 1044

& 연산자로 배열 num 첫 요소 주소를 포인터 ptr에 할당합니다.

int *ptr = &arr[0][0];

예제 1

포인터가 1 증가하면 다음 주소로 이동합니다. 3x4 배열 모든 12개 요소를 루프로 접근할 수 있습니다.

#include <stdio.h>
int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    // 포인터 ptr이 배열 num 가리킴
    int *ptr = &arr[0][0];
    int i, j, k = 0;
    // 포인터 ptr로 배열 num 요소 출력
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++) {
            printf("%d ", *(ptr + k));
            k++;
        }
        printf("\n");
    }
    return 0;
}

출력

1 2 3 4
5 6 7 8
9 10 11 12

일반적으로 배열 요소 주소는 다음 공식으로 구할 수 있습니다.

i행 j열 요소 주소 = baseAddress + [(i * 열개수 + j) * sizeof(array_type)]

우리의 3x4 배열에서,

arr[2][2] 주소 = 1000 + (2*4 + 2)*4 = 1044

위 그림 참조하면 arr[2][2] 주소가 1044임 확인 가능.

예제 2

역참조 포인터로 주소 값 가져오기. 이 공식으로 포인터 도움 받아 배열 순회.

#include <stdio.h>
int main() {
    // 2차원 배열
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    int ROWS = 3, COLS = 4;
    int i, j;
    // 포인터
    int *ptr = &arr[0][0];
    // 포인터 ptr로 배열 요소 출력
    for (i = 0; i < ROWS; i++) {
        for (j = 0; j < COLS; j++) {
            printf("%4d ", *(ptr + (i * COLS + j)));
        }
        printf("\n");
    }
    return 0;
}

출력

1 2 3 4
5 6 7 8
9 10 11 12

포인터와 3차원 배열

3차원 배열은 2차원 배열들의 배열입니다. 세 첨자로 선언.

int arr[x][y][j];

이 배열은 "x" 개 층으로, 각 층은 "x" 행 "y" 열 테이블로 볼 수 있습니다.

3차원 배열 예제:

int arr[3][3][3] = {
    { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
    { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
    { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} }
};

3차원 배열 포인터 선언:

int *ptr = &arr[0][0][0];

배열 이름이 0번째 요소 주소이므로, 3차원 배열 포인터를 이렇게 쓸 수도 있습니다.

int *ptr = arr;

"x" 행 "y" 열 각 층이 차지하는 바이트 수:

x * y * sizeof(data_type)

위 선언한 3차원 배열 arr 메모리 할당이 주소 1000부터 시작한다고 가정하면, 두 번째 층(i = 1)은 1000 + (3 * 3) * 4 = 1036 바이트 위치에서 시작.

ptr = 3차원 배열 arr 기준 주소

JMAX가 행 수, KMAX가 열 수면, 1번째 슬라이스 0행 0열 요소 주소는:

arr[1][0][0] = ptr + (1 * JMAX * KMAX)

i번째 슬라이스 j행 k열 요소 값 공식:

arr[i][j][k] = *(ptr + (i * JMAX * KMAX) + (j * KMAX + k))

예제: 포인터 역참조로 3차원 배열 출력

이 공식으로 포인터 역참조해 3차원 배열 출력.

#include <stdio.h>
int main() {
    int i, j, k;
    int arr[3][3][3] = {
        { {11, 12, 13}, {14, 15, 16}, {17, 18, 19} },
        { {21, 22, 23}, {24, 25, 26}, {27, 28, 29} },
        { {31, 32, 33}, {34, 35, 36}, {37, 38, 39} }
    };
    int JMAX = 3, KMAX = 3;
    int *ptr = arr; // &arr[0][0][0];
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            for (k = 0; k < 3; k++) {
                printf("%d ", *(ptr + (i * JMAX * KMAX) + (j * KMAX + k)));
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}

출력

11 12 13
14 15 16
17 18 19

21 22 23
24 25 26
27 28 29

31 32 33
34 35 36
37 38 39

일반적으로 포인터로 배열 접근은 첨자 표현으로 접근하는 것과 꽤 비슷합니다. 주요 차이는 첨자 배열 선언이 메모리를 정적으로 할당하는 반면, 포인터로는 동적 메모리 할당에 쓸 수 있다는 점입니다.

다차원 배열을 함수에 전달하려면 첨자 대신 포인터를 써야 합니다. 하지만 첨자 배열 사용이 포인터 사용보다 편리하며, 초보자에게 포인터는 어려울 수 있습니다.

C 언어

  1. C# 완전 개요: 마이크로소프트 모던 프로그래밍 언어의 특징과 강점
  2. C# Windows Forms 개발 실전 튜토리얼: 데스크톱 앱 만들기
  3. C# foreach 루프: 배열과 컬렉션 순회를 간편하게
  4. C# 열거형(Enum) – 정의, 예제, 활용법 완벽 가이드
  5. C 스코프 규칙: 지역, 전역, 정적, 매개변수 변수의 가시성과 수명
  6. C 토큰, 식별자, 키워드: 토큰과 그 유형 이해하기
  7. C while/do-while 루프 마스터: 실전 예제로 이해하기
  8. C# 중첩 클래스(Nested Class) 이해: 정의, 사용법, 상속
  9. C 구조체 마스터: 정의, 멤버 접근, 중첩, 정렬, 패딩, 비트필드
  10. C# 생성자(Constructor) 총정리: 종류, 예제, 고급 패턴