61.4 구조체와 구조체 포인터 반환하기

C 언어의 함수는 값을 하나만 반환할 수 있습니다. 그럼 인적 정보를 반환값으로 얻어오려면 어떻게 해야 할까요?

char *getName()
int getAge()
char *getAddress()

이름, 나이, 주소를 각각 얻어오려면 항목마다 함수를 만들어야 해서 비효율적입니다. 항목이 늘어나면 함수를 계속 만들어야겠죠?

이때는 함수에서 반환값으로 구조체를 활용하면 편리합니다. 함수에서 구조체를 반환하면 데이터를 묶어서 한 번에 가져올 수 있습니다.

struct 구조체이름 함수이름()
{
    return 구조체변수;
}

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

return_struct.c

#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strcpy 함수가 선언된 헤더 파일

struct Person {
    char name[20];
    int age;
    char address[100];
};

struct Person getPerson()    // Person 구조체를 반환하는 getPerson 함수 정의
{
    struct Person p;

    strcpy(p.name, "홍길동");
    p.age = 30;
    strcpy(p.address, "서울시 용산구 한남동");

    return p;    // 구조체 변수 반환
}

int main()
{
    struct Person p1;

    p1 = getPerson();    // 반환된 구조체 변수의 내용이 p1로 모두 복사됨

    // getPerson에서 저장한 값이 출력됨
    printf("이름: %s\n", p1.name);       // 홍길동
    printf("나이: %d\n", p1.age);        // 30
    printf("주소: %s\n", p1.address);    // 서울시 용산구 한남동

    return 0;
}

실행 결과

이름: 홍길동
나이: 30
주소: 서울시 용산구 한남동

구조체를 반환하는 함수를 정의하려면 struct Person getPerson()과 같이 struct 키워드와 구조체 이름을 함수 이름 앞에 붙여주면 됩니다. 그리고 다음과 같이 함수 안에서 Person 구조체 변수를 선언하고 값을 저장한 뒤 반환합니다.

struct Person getPerson()    // Person 구조체를 반환하는 getPerson 함수 정의
{
    struct Person p;

    strcpy(p.name, "홍길동");
    p.age = 30;
    strcpy(p.address, "서울시 용산구 한남동");

    return p;    // 구조체 변수 반환
}

main 함수에서 getPerson 함수를 호출하여 반환값을 변수 p1에 저장한 뒤 printfp1의 멤버를 출력해보면 getPerson에서 저장한 값들이 출력됩니다.

int main()
{
    struct Person p1;

    p1 = getPerson();    // 반환된 구조체 변수의 내용이 p1로 모두 복사됨

    // getPerson에서 저장한 값들이 출력됨
    printf("이름: %s\n", p1.name);      // 홍길동
    printf("나이: %d\n", p1.age);       // 30
    printf("주소: %s\n", p1.address);   // 서울시 용산구 한남동

    return 0;
}

구조체 변수를 반환한 뒤 다른 변수에 저장하면 반환된 구조체의 내용을 모두 복사하게 됩니다. 여기서는 Person 구조체의 멤버가 세 개뿐이라 큰 문제가 없습니다. 하지만 구조체 크기가 커지면 복사할 공간이 그만큼 더 필요하게 되어 공간이 낭비되므로 비효율적입니다.

참고 | 구조체 변수의 메모리 주소 반환?

함수가 끝나면 구조체 변수도 사라집니다. 따라서 & (주소 연산자)로 구조체 변수의 메모리 주소를 반환하면 안 됩니다.

구조체를 반환할 때는 구조체 복사가 일어나지 않도록 malloc 함수로 동적 메모리를 할당한 뒤 구조체 포인터를 반환하는 것이 좋습니다.

먼저 구조체 포인터를 반환하는 함수는 구조체 이름과 함수 이름 사이에 * (애스터리스크)를 붙이면 됩니다.

struct 구조체이름 *함수이름()
{
    return 구조체포인터;
}

다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

return_struct_pointer.c

#define _CRT_SECURE_NO_WARNINGS    // strcpy 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
#include <string.h>    // strcpy 함수가 선언된 헤더 파일
#include <stdlib.h>    // malloc, free 함수가 선언된 헤더 파일

struct Person {
    char name[20];
    int age;
    char address[100];
};

struct Person *allocPerson()    // Person 구조체 포인터를 반환하는 allocPerson 함수 정의
{
    struct Person *p = malloc(sizeof(struct Person));    // 구조체 포인터에 동적 메모리 할당;

    strcpy(p->name, "홍길동");
    p->age = 30;
    strcpy(p->address, "서울시 용산구 한남동");

    return p;    // 구조체 포인터 반환
}

int main()
{
    struct Person *p1;

    p1 = allocPerson();    // 포인터를 반환하여 p1에 메모리 주소 저장

    // allocPerson에서 저장한 값들이 출력됨
    printf("이름: %s\n", p1->name);       // 홍길동
    printf("나이: %d\n", p1->age);        // 30
    printf("주소: %s\n", p1->address);    // 서울시 용산구 한남동

    free(p1);    // 동적 메모리 해제

    return 0;
}

실행 결과

이름: 홍길동
나이: 30
주소: 서울시 용산구 한남동

먼저 struct Person *allocPerson()와 같이 반환값 자료형을 구조체 포인터로 지정해줍니다. 그리고 함수 안에서 구조체 포인터에 메모리를 할당하고 값을 저장한 뒤 구조체 포인터를 반환합니다.

struct Person *allocPerson()    // Person 구조체 포인터를 반환하는 allocPerson 함수 정의
{
    struct Person *p = malloc(sizeof(struct Person));   // 구조체 포인터에 동적 메모리 할당

    strcpy(p->name, "홍길동");
    p->age = 30;
    strcpy(p->address, "서울시 용산구 한남동");

    return p;    // 구조체 포인터 반환
}

이제 main 함수에서 allocPerson 함수를 호출한 뒤 반환된 포인터를 p1에 저장합니다. 이렇게 하면 p1에서 -> (화살표 연산자)로 멤버에 접근할 수 있고, 값을 출력해보면 앞에서 저장한 값들이 잘 출력됩니다. 즉, allocPerson에서 메모리를 할당한 뒤 메모리 주소가 들어있는 포인터만 반환하여 사용하므로 구조체의 내용을 모두 복사하지 않아서 훨씬 효율적입니다.

int main()
{
    struct Person *p1;

    p1 = allocPerson();    // 포인터를 반환하여 p1에 메모리 주소 저장

    // allocPerson에서 저장한 값들이 출력됨
    printf("이름: %s\n", p1->name);       // 이름: 홍길동
    printf("나이: %d\n", p1->age);        // 나이: 30
    printf("주소: %s\n", p1->address);    // 주소: 서울시 용산구 한남동

    free(p1);    // 동적 메모리 해제

    return 0;
}

메모리 사용이 끝났으면 free 함수로 해제를 해줍니다.

참고 | 구조체 별칭 사용하기

구조체 포인터 별칭(구조체 별칭)을 정의했다면 함수의 반환값 자료형에 구조체 포인터 별칭을 그대로 지정해주면 됩니다.

return_typedef_struct.c

typedef struct _Person {
    char name[20];
    int age;
    char address[100];
} Person, *PPerson;    // 구조체 별칭 Person, 구조체 포인터 별칭 PPerson

PPerson allocPerson()    // Person 구조체 포인터의 별칭을 반환값 자료형으로 지정
{
    PPerson p = malloc(sizeof(Person));    // 구조체 포인터에 동적 메모리 할당

    strcpy(p->name, "홍길동");
    p->age = 30;
    strcpy(p->address, "서울시 용산구 한남동");

    return p;    // 구조체 포인터 반환
}
참고 | 공용체와 열거형 반환하기

다음과 같이 함수에서 공용체와 열거형도 반환할 수 있습니다.

return_union_enum.c

#include <stdio.h>

union Box {
    short candy;
    float snack;
    char doll[8];
};

enum BOX_TYPE {
    BOX_PAPER = 0,
    BOX_WOOD,
    BOX_PLASTIC
};

union Box getBox()    // Box 공용체를 반환하는 getBox 함수 정의
{
    union Box b;  // 공용체 변수 선언

    b.candy = 10;

    return b;     // 공용체 변수 반환
}

enum BOX_TYPE getBoxType()    // BOX_TYPE 열거형을 반환하는 getBoxType 함수 정의
{
    return BOX_WOOD;
}

int main()
{
    union Box box;
    enum BOX_TYPE boxType;

    box = getBox();         // 반환된 공용체 변수의 내용이 b1로 모두 복사됨
    boxType = getBoxType();    // 반환된 열거형 값이 g1에 저장됨

    printf("%d\n", box.candy);    // 10: getBox에서 저장한 값
    printf("%d\n", boxType);      //  1: getBoxType에서 반환한 BOX_WOOD

    return 0;
}

지금까지 함수의 반환값에 대해 배웠습니다. 함수를 만들면 거의 대부분 값이나 포인터를 반환하도록 만들기 때문에 이 부분은 정확히 익히는 것이 중요합니다. 특히 포인터, 구조체 포인터 반환 부분은 문법이 복잡하므로 반복 학습하는 것이 좋습니다.