콘텐츠로 건너뛰기

배열과 포인터

변수라는 것부터 설명해서 배열과 포인터에 대해서 이야기 한다.

변수

데이타를 담아 둘 수 있는 문자이다. 데이타가 저장되는 메모리의 주소는 ‘&’문자(&=주소연산자)를 변수명 앞에 붙여서 찾을 수 있다. 참고로 자료형선언없이 주소변수 앞에 붙는 “*”문자(*=역참조연산자)를 변수명 앞에 붙이면

자료형

데이타의 형식이다. int, float 형 및 int*, float* 형 등이 있다. 배열형은 없다.

int a; // a라는 변수에 저장되는 데이타의 형식인 int형임을 의미한다.

int* a; // a라는 포인터변수에 저장되는 데이타의 형식이 int형임을 의미한다. int *a와 같다.

그런데 int*을 자료형이라고 한 것은, a라는 문자에 저장되는 값이 int가 아니라 주소값이기 때문이다.

즉, int* a; 라는 의미는 int형 데이타를 가리키는 주소 a를 선언한 것이기 때문이다.

그래서 윗 식은 항상 int형 데이타를 저장하는 포인터 변수 a를 초기화한다라고 이해해야 한다.

변수선언 및 접근

  • 일반변수

int a; // a라는 변수를 선언했는데 a에는 int형 자료를 넣을 수 있다.

a=1; // a에 1을 대입한다.

  • 포인터변수

int a;

int* ptr = &a; // ptr라는 포인터 변수를 선언했는데 이 포인터 변수는 int형 데이타를 저장하는 주소를 담고 있다.

#include <stdio.h>

int main() {
    int a=10;    
    int* ptr; ptr=&a;
    // 윗 문장은 int* ptr = &a;와 같다.
    printf("%d\n\n", *ptr); //*ptr에서 *이 역참조연산자이다.
    return 0;
}

변수의 정의를 보면 데이타를 저장하는 문자라고 했으니까 ptr은 주소데이타를 저장하고 있는 문자라고 보면된다. 그래서* 

int* p; or int *p:

p라는 포인터 자료형을 선언했는데, p는 주소를 저장하고 이 주소는 int 자료형을 저장하는 공간의 주소라는 의미이다. 다른 말로는 이러한 int 자료형 공간을 포인팅해주는 변수라는 의미이다.

int a=1;

int* p= &a;

라는 선언을 볼 수 있다. 이것은 

int a=1;

int* p;

p=&a;

와 같다.

참조로,

p=&a의 양변에 역참조 연산자 *를 넣으면,

*p=*(&a);

즉, *p==a가 된다.

*배열설명 시작

이러한 int형 자료가 여러개 있는데 이것을 하나의 변수로 접근하려고 한다. 그러면 배열생성 연산자를 이용하면 된다.

int a[3];

이것은 int 자료형을 가지는 배열 3개를 만드는데 그 시작주소는 a라는 의미이다.

왜 배열을 먼저 만드느냐는 연산자 우선순위가 높기 때문이다.

C언어의 연산자 우선순위를 보면 (), [] 등이 최우선 순위를 가진다고 나온다. 그런데 ()는 이해가 간다. 괄호로 묶인 내용을 먼저 처리해야 한다는 의미이다. 그런데 []? 이것은 배열생성 연산자이면서 첨자 연산자이다.

그리고 매우중요한 이야기!

a가 시작주소를 가진다는 의미는 a는 포인터라는 의미이다.

데이타는 a[0], a[1]과 같이 배열이름에 첨자연산자(인덱스연산자)를 마지막에 넣으며 인덱스값을 인덱스연산자 안에 넣어주면 된다.

a[0]=1;

a[1]=2;

위에서는 인덱스연산자이므로 

a+0의 주소는 a 시작주소 값이다.

a+1의 주소는 a 시작주소 + sizeof(int)의 값이다.

이것은 a가 포인터변수이기 때문이다.

즉,  

int a[3];

int* ptr=a;

또는

int a[3];

int* ptr;

ptr=a;

**2차원 배열

2차원 배열은 행과 열을 가진다.

int a[2][3];

배열생성 연산자인 [2][3]을 이용해서 배열을 만들고 그 시작주소는 a가 가진다.

즉 a라는 포인터를 사용하겠다는 의미이다.

이런 관점에서 나온것이 배열 포인터이다. 즉, 배열을 가리키는 포인터를 이용하겠다는 의미이다.

int (*arrPointer)[3];

연산자 우선순위에 의해서

int 자료형 배열 3개를 만들고 이 배열을 arrPointer라는 포인터로 접근하겠다라는 의미이다.

이러면 각 행의 갯수는 마음대로 정할 수 있다.

그리고, 괄호를 묶은 이유는

int* arrPointer[3];

로 선언해 놓으면, 이것은 int 자료형을 가리키는 포인터를 담고 있는 배열 3개를 만들라는 의미로 해석되기 때문이다.

정리하면, 배열 포인터는 배열의 우선순위가 높기 때문에 int 자료형 값을 저장하는 배열부터 만들고, 이 배열의 시작주소를 포인터로 선언하는 것이다.

반면에, 포인터 배열은 int* 이라는 자료형을 가지는 배열을 만들고 이 배열에 주소를 넣겠다라는 의미이다.

아래는 배열포인터의 예와 포인터배열의 예를 설명해주는 코드이다. 모두 2차원 배열에 접근할 수 있다.

*배열포인터

#include <stdio.h>

int main() {
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};

    // 배열 포인터 선언
    int (*arrPointer)[3] = matrix;

    // 배열 포인터를 사용하여 값 출력
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("%d ", arrPointer[i][j]);
        }
        printf("\n");
    }

    return 0;
}

위 코드를 보니 만들어진 배열에 접근하기 위해서는 배열과 포인터를 연결해주면 된다.

int (*arrPointer)[3] = matrix;

그러나 포인터 배열은 행 주소를 별도로 받아 들여야 한다.

*포인터배열

#include <stdio.h>

int main() {
    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};

    // 포인터 배열 선언
    int *ptrArray[2];

    // 포인터 배열 초기화
    for (int i = 0; i < 2; ++i) {
        ptrArray[i] = matrix[i];
    } // 행주소를 일치시켰다.

    // 포인터 배열을 사용하여 값 출력
    for (int i = 0; i < 2; ++i) {
        for (int j = 0; j < 3; ++j) {
            printf("%d ", ptrArray[i][j]);
        }
        printf("\n");
    }

    return 0;
}

윗 장점은 행 주소를 연결해주는 작업이 필요하나 열의 크기가 가변적일 때 사용할 수 있다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다