C 언어 | 구조체 선언 | 구조체의 멤버

구조체의 멤버에 다른 구조체 형식을 지정할 수 있다. 여러 구조를 조합한 복잡한 구조와 구조체에 대한 포인터를 멤버로 가지는 구조체 등을 만들 수 있다.

구조체 멤버를 가지는 구조체

구조체는 멤버에 다른 구조체를 포함할 수 있다. 이것은 구조체의 특성에 따라 편리한 경우가 있다. 예를 들어, 좌표를 나타내는 Point 구조체와 크기를 나타내는 Size 구조체가 있다고 하여, 이러한 구조체의 멤버를 포함한 Rectangle 구조체를 만들 수 있다.

struct Point { int x , y; };
struct Size { int width , height; };
struct Rectangle {
  struct Point location;
  struct Size size;
};

Rectangle 구조체는 Point 및 Size 구조체의 멤버를 포함한다. 사각형을 나타내는 Rectangle의 정보는 좌표와 크기로 구성되어 있기 때문에 이러한 형태가 유용하다고 생각된다. 물론 실전용을 생각하면 멤버를 추적하는 것이 귀찮아서 단순히 4개의 int 형 멤버를 선언해야 한다는 주장도 있을 것이다. Rectangle의 폭 width를 참조하려면 다음과 같이 작성한다.

rect.size.width
rect->size.width

위는 일반 변수이고, 아래는 포인터에서 액세스한 경우이다. 따라서, 가장 바깥 쪽의 구조체부터 멤버를 차례로 접근해 간다.

코드1

#include <stdio.h>

struct Point { int x , y; };
struct Size { int width , height; };
struct Rectangle {
  struct Point location;
  struct Size size;
};

int main() {
 struct Rectangle rect = { 100 , 50 , 400 , 300 };

 printf("Location (%d , %d) : Size (%d , %d)\n" ,
   rect.location.x , rect.location.y ,
   rect.size.width , rect.size.height
  );
  return 0;
}

이 프로그램은 Point 및 Size 구조체를 포함하는 Rectangle 구조체를 선언하고 이를 이용하고 있다. 초기화를 보면 인식할 수 있다고 생각하지만, 비록 구조체의 멤버를 가져도 기본적인 개념는 동일하며, Rectangle 구조체의 실체는 int 형 4개라는 사실은 변하지 않을 것이다 .

이 방법을 사용하여 “구조체 형변환 코드2"를 개선하고, 보다 확실하게 기본이 되는 구조를 상속할 수 있다.

코드2

#include <stdio.h>

struct Color {
  char *name;
 int r , g , b;
};
struct ColorEx {
  struct Color color;
 int a;
};

void SetColor(struct Color *color) {
  printf(
   "%s r = %d : g = %d : b = %d\n" ,
    color->name , color->r , color->g , color->b
  );
}

int main() {
 struct Color color = { "Color" , 0xFF , 0 , 0 };
  struct ColorEx colorEx = { "ColorEx" , 0 , 0xFF , 0 , 0xA0 };

 SetColor(&color);
 SetColor((struct Color *)&colorEx);

 return 0;
}

ColorEx 구조체의 첫번째 멤버가 Color 구조체인 것에 주목해 보자. 이전은 Color 구조체와 동일하게 되도록 name, r, g, b를 각각 다시 정의하고 있지만, 코드2의 ColorEx는 보다 확실하게 기본이 되는 Color 구조체를 계승하고 있다. 디자인으로써는 이러한 작성하는 것이 일관성이 높아지기 때문에 안전성이 향상된다고 생각할 수 있다.

자기 참조적 구조

구조체가 구조체의 멤버를 보유할 수 있는 것을 알 수 있었다. 그럼 그 구조체 자신을 멤버로 보유한 경우는 어떻게 될까. 이러한 구조체를 자기 참조적 구조라고 한다. 예를 들어 다음과 같은 구조의 선언이다.

struct Node { struct Node node; };

유감스럽게도, 이것은 오류이다. 그러나 포기는 이르다. 자기 참조의 목적은 구조체의 멤버로서 자신과 같은 정보를 가진 존재에 액세스할 수 있다. 구조체은 포인터 형의 멤버를 보유할 수 있기 때문에 다음의 선언이면 유효하다.

struct Node { struct Node *node; };

이 Node 구조체는 Node 형에 대한 포인터를 보유한다. 이 포인터에서 간접 참조함으로써 자기 참조적인 구조를 만들 수 있다. 자기 참조적인 구조체는 친자 관계의 존재하는 개체을 만들때 적합하다. 예를 들어, 윈도우 시스템의 컴포넌트 관계 및 메뉴와 트리로 사용되는 항목의 계층 관계이다. 트리 구조의 성질을 가진 정보를 취급하는 경우 동질의 부모와 자녀를 보유할 가능성이 높고, 이러한 정보의 관리는 자기 참조적인 구조체가 유용할 것이다.

#include <stdio.h>

struct Chain {
 char *text;
 struct Chain *next;
};

void ShowChain(struct Chain *chain) {
  if (chain == NULL) return;

  printf("%s\n" , chain->text);
  ShowChain(chain->next);
}

int main() {
  struct Chain first = { "First Chain" };
 struct Chain second = { "Second Chain" };
 struct Chain third = { "Third Chain" };

 first.next = &second;
 second.next = &third;

 ShowChain(&first);

  return 0;
}

코드3은 텍스트 데이터의 관련을 연결하는 자기 참조적인 구조체 Chain을 선언하고 있다. 이 구조체를 이용하면, 자기 참조하여 바로 체인과 같은 데이터 관계를 만들 수 있다. ShowChain() 함수는 주어진 Chain 포인터에서 체인을 접근하면서 요소가 없어질 때(next 멤버가 NULL)까지 재귀 프로세스를 반복한다.




최종 수정 : 2017-11-26