구조체란 변수를 묶어 새로운 자료형을 정의하는 것이다. 이 때 변수는 포인터와 배열을 모두 포함한다.
예를 들어 x좌표와 y좌표를 묶어 구조체를 정의하면 두 데이터를 함께 관리하고 사용하는 것이 편리해진다. 다음과 같이 선언할 수 있다.
struct point{
int xpos;
int ypos;
};
다음은 사람의 정보를 묶어 만든 구조체이다.
struct person{
char name[10];
char phoneNum[20];
int age;
};
새로 정의한 구조체 point와 person은 하나의 자료형이 되는 것이다. 이러한 자료형을 사용자 정의 자료형이라 한다. 구조체 변수의 선언은 다른 자료형과 같은 방식을 사용하되, 앞에 struct 키워드를 붙여주면 된다.
구조체 변수의 선언은 다음과 같은 형식을 따른다.
struct type name;
위에서 정의한 사용자 정의 자료형으로 구조체 변수를 선언하려면 다음과 같이 작성하면 된다.
struct point pos; //point형 변수 pos
struct person man; //person형 변수 man
생성된 구조체 변수는 다음의 형태로 존재한다.
구조체 내에 존재하는 멤버에 접근하려면 .
연산자를 이용해 name.member
와 같이 접근하면 된다.
구조체 변수를 선언과 동시에 초기화할 수 있다.
struct point pos = {10, 10};
struct person man = {"홍길동", "010-1234-5678", 30};
구조체를 배열로 묶을 수도 있다. 일반적인 배열과 같이 선언하면 된다.
길이가 4인 구조체 배열 arr를 선언하면 다음과 같이 배열이 할당된다.
struct point arr[4];
구조체 포인터 변수는 일반적인 포인터 변수와 다르지 않다. 다음 예시를 보자.
struct point pos = {10, 10};
struct point *ptr = &pos;
(*ptr).xpos = 20;
(*ptr).ypos = 20;
혹은 이렇게 접근할 수도 있다.
ptr->xpos = 20;
ptr->ypos = 20;
두 표현은 동일하다.
앞서 말했듯이 구조체 멤버로 포인터 변수가 올 수 있다. 여기서, 자기 자신의 자료형의 포인터 변수도 구조체 멤버가 될 수 있다. 다음과 같은 선언이 가능하다.
struct point{
int xpos;
int ypos;
struct point * ptr;
};
삼각형의 세 꼭짓점의 연결 관계를 나타낸 다음 예제를 자세히 읽어가며 공부해보자.
👉🏻구조체에서, 구조체 변수의 주소값과 첫 번째 멤버의 주소값은 동일하다.
구조체 변수를 선언할 때, struct 키워드를 붙여야만 한다. 하지만 struct 키워드 없이 선언할 수 없을까? typedef 선언을 추가하면 된다.
typedef는 기존에 존재하는 자료형에 새 이름을 부여하고 싶을 때 사용한다. 다음 예시를 보자.
typedef int INT; //INT는 자료형 int의 또 다른 이름이 됨
INT num; // int num;과 동일한 표현
INT *ptr; //int *ptr;과 동일한 표현
이러한 방법을 통해 unsigned int , unsigned int *등 길고 복잡한 자료형을 간단하게 표현할 수 있다.
구조체도 typedef 선언을 통해 또 다른 이름을 붙여줄 수 있다.
typedef struct point Point;
Point pos; //struct point pos;보다 간결
구조체를 정의할 때 typedef 선언을 함께 할 수 있으며, 이러한 방식을 많이 사용한다. 다음과 같이 작성하면 된다.
typedef struct point{
members
} Point;
위와 같이 typedef로 새 이름을 부여하는 경우, 구조체 이름인 point는 필요하지 않다. 따라서 아래와 같이 생략 가능하다.
typedef struct
members
} Point;
구조체 또한 함수에 인자로 전달할 수 있고, 리턴타입이 될 수 있다. 구조체 변수가 함수로 전달되면, 값이 통째로 매개변수에 복사되는 값에 의한 호출이 발생한다. 구조체의 멤버로 배열이 선언된 경우에도 이는 동일하다. 값이 리턴될 때 역시도 배열이 통째로 복사된다.
구조체 변수를 함수의 인자로 전달할 때, 값에 의한 호출과 참조에 의한 호출 모두 가능하다.
👉🏻구조체 변수 연산
기본 자료형 변수를 대상으로 가능한 많은 연산들이 구조체 변수를 대상으로는 불가하다.
구조체 변수는 대입연산, sizeof 연산 등이 가능하지만, 사칙연산은 불가하며 작성자가 직접 함수를 정의해야한다.
먼저 구조체를 사용하는 이유에 대해 생각해보면, 구조체를 통해 관련된 데이터들을 하나의 자료형으로 묶어 관리하기 위함이다. 예를 들어, 구조체 배열을 이용하면 하나의 배열로 각 원소에 딸린 데이터를 쉽게 저장하고 다룰 수 있다.
이 때, 중첩 구조체는 구조체 안에 구조체 변수가 멤버로서 존재하는 것을 말한다. 다음 예제를 통해 중첩 구조체의 활용을 알아보겠다.
union 키워드를 사용해 정의하는 공용체는 정의, 선언 방법은 구조체와 동일하나 메모리 할당 방식은 다르다.
위와 같이 구조체와 공용체를 선언했을 때, 메모리 상에는 다음과 같이 저장된다.
구조체 변수의 멤버는 각각 공간을 차지하지만 공용체 변수의 멤버는 크기가 가장 큰 멤버의 변수 하나만이 실질적으로 할당되어 모든 멤버들이 이 공간을 공유하게 된다.
하나의 변수 공간만을 사용하므로, 멤버들끼리 같은 주소공간 같은 값을 공유하고, 초기화하거나 새로운 값을 저장할 때에는 덮어쓰기해야 한다. 다음 예시를 보면 이를 확인할 수 있다.
구조체가 존재하는데 왜 공용체를 사용하는가?
공용체가 유용하게 사용되는 다음의 사례를 보면 이해할 수 있다.
열거형도 구조체나 공용체처럼 데이터를 한 데 묶어 새로운 자료형을 만드는 방법이다. 하지만 열거형은 해당 자료형에 값을 직접 저장한다는 차이가 있다. 다음과 같이 선언한다.
enum syllable{
Do=1, Re=2, Mi=3, Fa=4, So=5, La=6; Ti=7
};
또는 typedef를 이용하여 선언할 수 있다.
typedef enum syllable{
Do=1, Re=2, Mi=3, Fa=4, So=5, La=6; Ti=7
}Syllable;
마치 서로 연관된 상수 선언을 묶어놓은 것과 같이 생각되는 자료형이다.
다음은 열거형을 이용한 예제이다.
열겨형 상수의 값은 명시되어있지 않으면 0에서부터 1씩 증가하는 형태로 결정된다.