C++ 동적 구조체 | C++ 자습서 15

C++ 동적 구조체

동적 구조체를 동적 할당하는 개념은 기본은 변수나 배열의 포인터를 선언하여 동적으로 할당하는 것과 같습니다. 동적으로 할당하는 이유는 데이터의 크기가 런타임(실행시간)에 변화하는 것에 유연하게 대응하기 위해서 입니다.

이 런타임(runtime)과 컴파일타임(compile)이 자주 등장하는데요. 이들에 대해서는 다음 포스트에서 상세히 알아볼 것 입니다.

이번 포스팅에서는 구조체의 동적 할당을 알아보겠습니다. 말이 복잡하지만 구조체를 정의한 후 실행시간에 메모리를 할당 받아서 사용한 후 돌려주는 것 입니다.

C에서는 malloc 함수를 사용했는데요. C++에서는 new 키워드(연산자)를 사용합니다. 구조체와 클래스는 같은 구조기 때문에 동적 구조체를 잘 이해하면 클래스도 상당 부분 자동으로 이해가 됩니다.

동적 구조체 예제

아래는 두개의 예제 코드 입니다. 첫번째는 구조체 동적 할당이고 두번째는 구조체 배열의 동적 할당입니다. 둘다 구조체 포인터를 사용합니다.

첫번째 구조체는 person* ps = new person; 로 포인터에 구조체 데이터 타입의 메모리를 할당합니다. 누누히 강조하지만 모든 포인터는 주소값을 저장합니다. 32비트 주소값은 모두 4바이트 입니다. 즉 포인터는 항상 4바이트라는 것이죠. 32바이트 짜리 구조체를 저장하건 클래스를 저장하건 4바이트입니다. sizeof 연산자를 써서 확인할 수도 있죠.

포인터가 32비트 주소로 4바이트라는 것을 생각하면 좀더 이해가 쉽습니다. 주소를 저장한다는 개념을 갖추면 메모리에 대한 강력한 제어를 할 수 있습니다.

1번에서 보면 포인터 ps 에 -> 멤버십 연산자를 사용하여 값에 접근하고 또 하나는 (*ps) 에 도트연산자를 사용해서 접근할 수 있습니다. 구조체의 이름이 없으면 . 도트 연산자를 사용할 수 없고 -> 멤버십 연산자를 사용해야 합니다. 그런데 ugly 하지만 (*ps) 처럼 괄호로 감싸면 역참조로 직접 멤버에 접근할 수 있습니다.

구조체의 이름은 주소가 아니라 사용할 수 없는 형식입니다. 이 부분이 조심해야 하는 부분인데요. 포인터를 사용할 때는 이름이 없이 주소로만 접근하기 때문에 그런일이 없는데 구조체의 이름을 사용하려고 하면 컴파일러에서 말리지 않기 때문에 이상한 결과가 나옵니다.

#include <iostream>

using namespace std;

struct person {
    char name[30];
    int age;
    char gender;
};


int main()
{

    // 1. 구조체 동적 할당

    person* ps = new person;

    // ps 는 포인터이므로 도트연산자 . 사용불가
    strcpy(ps->name, "Jake");
    ps->age = 17;
    ps->gender = 'M';

    cout << "\n[------- dynamic struct allocation -------]\n";
    // 역참조를 하면 도트연산자 사용가능
    cout << "- name  : " << (*ps).name << endl;
    cout << "- age   : " << (*ps).age << endl;
    cout << "- gender: " << (*ps).gender << endl;

    delete ps;

    // 2. 구조체 동적 배열 초기화. Reuse

    ps = new person[3]{
        {"Mike", 15, 'M'},
        {"Jinn", 16, 'F'},
        {"Waldo", 17, 'M'}
    };

    cout << "\n[------- first one dynamic -------]\n";
    cout << "- name  : " << ps->name << endl;
    cout << "- age   : " << ps->age << endl;
    cout << "- gender: " << ps->gender << endl;

    cout << "\n[------- Second one dynamic -------]\n";
    cout << "- name  : " << (ps+1)->name << endl;
    cout << "- age   : " << (ps+1)->age << endl;
    cout << "- gender: " << (ps+1)->gender << endl;

    // Ugly Style but it's working
    cout << "\n[------- Third one dynamic -------]\n";
    cout << "- name  : " << (*(ps+2)).name << endl;
    cout << "- age   : " << (*(ps+2)).age << endl;
    cout << "- gender: " << (*(ps+2)).gender << endl;

    // 이번은 배열이니까 []로 배열 메모리 반환
    delete[] ps;

    return 0;

}
[------- dynamic struct allocation -------]
- name  : Jake
- age   : 17
- gender: M

[------- first one dynamic -------]
- name  : Mike
- age   : 15
- gender: M

[------- Second one dynamic -------]
- name  : Jinn
- age   : 16
- gender: F

[------- Third one dynamic -------]
- name  : Waldo
- age   : 17
- gender: M

두번째는 구조체 배열의 사용법입니다. ( ) 괄호가 많이 들어갑니다. 일부러 각 구조체 요소를 다른 방법으로 출력해봤습니다. ( ) 괄호가 많이 들어갔을 때는 하나씩 까봐야 합니다. C++ 에서 포인터를 사용하다 보면 이 연산자들 때문에 골머리를 앓습니다.

다른 언어에 비해서 유독 기호가 많죠. 방심했다간 헷갈리기가 쉽습니다. 이렇게 다양한 접근 방법이 있다는 점을 이해한 후 실습을 통해 어느정도 손에 익숙해질 필요가 있습니다. 아무리 머리로 이해가 되도 연습이 부족하면 코드를 작성할 때 손이 안따라갈 수 있습니다.

요약

C++ 동적 구조체의 사용법을 알아봤습니다.

Tricky(함정같은) 문법이 많습니다. 클래스를 생성할 때도 같이 적용되는 규칙들입니다.

익숙해지면 좋을 것 같습니다. 그리고 메모리 delete 하는 것도 잊지 않습니다.

참고문서

Dynamic array of structures – C++ 동적 구조체 (cplusplus.com)

Leave a Comment