반응형

구조체의 값도 주소를 가지고 있기 때문에 구조체를 포인터로 처리할 수 있다. 구조체 포인터에서 구조를 가진 멤버에 액세스하는 방법을 설명한다.

구조체의 멤버에 간접 참조

구조체의 멤버가 포인터의 경우는 일반 포인터와 그 다루는 것은 변하지 않는다. 그러나 구조체의 포인터를 다룰 때 참조 방법에 주의해야 한다. 구조체 형의 포인터는 구조체 변수(인스턴스)의 메모리 주소를 저장한다. 이것은 "포인터"에서 설명된 일반 변수에 대한 포인터와 동일하다. 다음과 같은 구조체 선언을 생각해 보자.

struct Point *pointer = &pt;

pointer는 Point 형 구조체 변수의 주소를 저장하는 포인터 변수이다. 포인터를 이해하고 있으면, 이 선언과 초기화에 대해서는 특히 의문은 없을 것이다. 문제는 이 포인터가 가리키는 구조체 멤버에 간접 참조하려면 어떻게 하는가하는 것이다. 단순히 구조체의 인스턴스를 취득하는 경우는 간접 연산자를 사용뿐이다.

struct Point pt = *pointer;

그러나 구조체의 역할을 생각하면, 일반적으로 구조체의 인스턴스를 반환하는 목적이 아니라, 구조체의 인스턴스 멤버에 액세스하기 위해 간접 참조를 하는 것이다. 처음에는 다음과 같은 방법으로 접근을 시도 할지도 모른다.

int x = *pointer.x ;

언뜻 보면 pointer 포인터 변수가 가리키는 구조체의 x 멤버에 액세스하는 것처럼 보이지만, 불행히도 다르다. 이 경우 구조체의 x 멤버를 간접 참조하고 있음을 나타낸다. 구조체의 멤버가 포인터 형의 경우에 이러한 구문이 사용된다. 예를 들어 다음과 같은 구조이다.

struct Point {
    int *x;
    int *y;
};

하지만 이번 목표는 포인터 형의 멤버에서 간접 참조를 실시하는 것이 아니라 구조체의 포인터에서 구조체의 인스턴스에 간접 참조하는 것이다. 이 경우는 연산자 우선 순위의 관계에서 다음과 같이 작성해야 한다.

int x = (*pointer).x;

이렇게 하여 구조체 형의 포인터에서 간접 참조할 수 있다. 위의 문장은 (*pointer)가 구조에 간접 참조를 실시하는 구조체의 인스턴스를 반납하고, 그 x 멤버에 액세스하는 것을 나타낸다.

코드1

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point pt = { 100 , 200 };
    struct Point *ppt = &pt;

    printf("pt.x = %d : pt.y = %d\n" , (*ppt).x , (*ppt).y);
    return 0;
}

실행결과

pt.x = 100 : pt.y = 200

코드1은 Point 형의 포인터 ppt 구조체 변수 pt 주소를 대입한다. printf() 함수에서 포인터에서 구조체의 멤버 값을 표시하기 위해 (*ppt) .x라고 하는 형태로 간접 참조를 하고 있는 것에 주목하자.

구조체에 대한 포인터는 실제 프로그래밍에서 자주 사용된다. 거대한 시스템에서는 이것인가?라고 말할 만큼 구조체가 사용되고 있으며, 이 구조체의 정보 전달과 생산, 가공을 함수로 하는 수단으로 포인터에 의한 참조 전달이 사용되는 것이다. 구조체의 포인터를 간접 참조할 때 일부러 위와 같이 (*pointer).~라고 작성하는 것은 조금 귀찮다.

그래서 멤버 액세스 연산자 "->"를 사용할 수 있다. 이 연산자는 기능적으로 "."연산자와 동일하지만 구조체의 포인터에서 멤버를 참조한다는 점에서 성격이 다르다. 멤버 액세스 연산자 "->"는 종종 화살표 연산자라고 한다.

화살표 연산자

구조체에 대한 포인터명 -> 멤버명

이 -> 연산자는 빼기 -와 > 기호의 조합으로 구성되어 있다. (*pointer) .member과 pointer->member와 똑같은 것이라고 생각할 수 있다. 많은 프로그래머는 (*pointer).member보다 pointer->member라는 기법을 선호하기 때문에 특별한 의도가 없다면 -> 연산자를 사용하는 것을 권장한다.

코드2

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point pt = { 100 , 200 };
    struct Point *ppt = &pt;

    printf("pt.x = %d : pt.y = %d\n" , ppt->x , ppt->y);
    return 0;
}

이 프로그램의 동작은 코드1과 동일하지만, 구조에 대한 포인터 ppt에서 멤버를 참조하면 -> 연산자를 사용한다는 점에서 차이가 있다.

"구조체의 코드7"에서 함수에 구조체의 값 전달은 하지 않는다고 설명하였다. 이유는 값을 통째로 복사하므로, 합성체의 값 전달은 메모리와 CPU에 높은 부하를 가할 가능성이 있다. 일반적으로 배열이나 구조체 등은 포인터에 의한 참조 전달을 채용한다. 이번 해설한 구조체의 포인터를 다루는 방법을 이해하면 이를 구현할 수 있을 것이다.

#include <stdio.h>

struct Point {
    int x;
    int y;
};
void SizeToPoint(struct Point *offsetPoint , struct Point *target , int width , int height) {
    target->x = offsetPoint->x + width;
    target->y = offsetPoint->y + height;
}

int main() {
    struct Point location = { 100 , 100 } , target;
    SizeToPoint(&location , &target , 200 , 40);

    printf(
        "Rectangle\n\t"
        "Left = %d : Top = %d\n\t"
        "Right = %d : Bottom = %d\n" ,
        location.x , location.y , target.x , target.y
    );
    return 0;
}

이것은 "구조체의 코드7"의 SizeToPoint() 함수를 개량한 것이다. 정보원이 되는 기준점을 포함하는 구조체에 주소를 offsetPoint 지정하고 새로운 좌표를 저장하는 구조체의 주소를 target에 지정한다. width와 hieght은 지금까지와 같이 offsetPoint 대한 폭과 높이를 지정란다. 이 함수는 offsetPoint 매개 변수에서 상대적인 폭과 높이의 위치를 나타내는 좌표를 target 매개 변수에 간접 참조로 저장한다.

SizeToPoint()를 호출 할 때, 구조체의 인스턴스가 아닌 포인터를 전달하고 있기 때문에, 합성체를 통째로 복제하는 부하를 피할 수 있다. 또한 반환 값으로 구조체를 반환하는 것이 아니라, target을 간접 참조 값을 대입하고 있기 때문에, 반환 값은 사용되지 않는다. 예를 들어, 함수가 성공했는지 실패했는지를 호출자에게 통지하는 수단으로 사용하는 등 다른 용도로 반환 값을 사용할 수 있게 된다.

반응형

+ Recent posts