배열은 서로 같은 타입의 데이터를 묶는다면 구조체는 타입이 다른 데이터를 묶는 방법이다.
구조체의 멤버에 접근 하기 위해서는 멤버 참조 연산자(.)을 이용한다.
#include <stdio.h>
#include <string.h>
struct person{
char name[10];
int age;
float height;
};
// 구조체 변수 선언
struct person a;
int main(){
struct person a;
strcpy(a.name, "tom");
a.age = 20;
a.height = 180.5;
printf("%s %d %lf",a.name, a.age, a.height);
}
결과 : tom 20 180.500000
멤버 하나씩 초기화 할 경우 번거롭기 때문에 중괄호({})을 이용해 초기화 하는 방법도 있다.
struct person a = {.name = {"홍길동"}, .age = 24, .height = 180};
struct person a = {"홍길동", 24, 180};
#include <stdio.h>
#include <string.h>
// 구조체 정의
typedef struct person{
char name[10];
int age;
float height;
} person;
// 구조체 대입 검사 프로그램
void main(){
person a = {"홍길동,", 24, 180};
person b;
b = a; // 하나의 구조체 변수의 내용을 다른 구조체에 대입할 수 있는지 여부 -> 가능
printf("%s %d %lf ",b.name, b.age, b.height);
}
void main(){
person a = {"홍길동,", 24, 180};
person b = {"고길동", 30, 175};
if(a>b){
printf("A가 B보다 크다\\n"); //구조체 변수끼리 비교 => 불가능
//구조체 멤버 변수 끼리 비교 => 가능
}
}
구조체 선언 할 때 sturct 구조체이름 {구조체 멤버 };
형태로 선언하고 main함수 내에서 struct [구조체 이름] [변수 이름]
과 같이 선언
하지만 더 편리한 방법이있다. typedef 키워드를 사용해 구조체 이름을 생략 할 수 있다.
typedef 키워드는 C언어에서 자료형을 새로 정의 할 때 사용하는 키워드이다. typedef를 사용하면 main함수에서 구조체를 선언 할때 매번
struct
를 써줄 필요가 없다. typedef를 이용하면 구조체 별칭이 필요한데, 구조체 별칭은 구조체 정의 할 때 중괄호 뒤에 써주면 된다.
익명구조체 : typedef을 사용하면 구조체 이름을 생략 가능하다. 별칭만 사용하고 구조체 이름을 생략 할 수 있다.
#include <stdio.h>
struct name
{
char first[30];
char last[30];
};
struct friends
{
struct name friend_name; //구조체 중첩
char address[30];
char job[30];
};
int main(){
struct friends hong = {"길동", "홍", "서울시 강남구 역삼동", "학생"};
printf("%s\\n\\n", hong.address);
printf("%s%s\\n", hong.friend_name.last, hong.friend_name.first);
return 0;
}
#include <stdio.h>
typedef struct
{
char first[30];
char last[30];
}name;
typedef struct
{
name friend_name;
char address[30];
char job[30];
}friends;
int main(){
friends hong = {"길동", "홍", "서울시 강남구 역삼동", "학생"};
printf("%s\\n\\n", hong.address);
printf("%s%s\\n", hong.friend_name.last, hong.friend_name.first);
return 0;
}
자체 참조 구조체(self-referential structure) : 특별한 구조체로 구성 요소 중에 자기 자신을 가르키는 포인터가 한개 이상 존재하는 구조체를 말한다.
왜 사용할까
- 연결 리스트 or 트리를 구성할 때 사용
- 항목의 개수를 미리 예측할 수 없는 경우 자체 참조 구조체를 정의해 놓고 동적으로 기억장소를 할당 받아 이들을 포인터로 연결하여 자료구조를 구성
struct list
{
int data;
struct list* next;
};
자기 참조 구조체에서 next와 같이 자기 자신의 구조체를 가르키는 포인터 변수를 링크(link)라고 한다.
자기 자신의 구조체를 가르치는 Next포인터를 통해 연결을 해보자
#include <stdio.h>
// 구조체 정의
typedef struct list{
int data;
struct list *next;
}list;
void main(){
list a, b, c;
a.data = 1;
b.data = 2;
c.data = 3;
a.next = &b;
b.next = &c;
c.next = NULL;
printf("%d\\n", a.data);
printf("%d\\n",a.next->data);
printf("%d\\n", a.next->next->data);
}
위와같이 자기 참조 구조체가 링크를 통해 차례로 연결되어 만들어진 구조를 연결 리스트(linked list)라 한다. 연결 리스트를 이루는 각 구조체를 노드(node)라고 한다.
반복문을 이용해 노드의 값을 출력하기
#include <stdio.h>
// 구조체 정의
typedef struct list{
int data;
struct list *next;
}list;
void main(){
list a, b, c, d;
list *head; //맨처음을 가르키는 구조체가 무엇인지 저장하는 구조체
head = &a; //맨 처음 구조체인 구조체의 시작 주소 값
a.data = 1;
b.data = 2;
c.data = 3;
d.data = 4;
a.next = &b;
b.next = &c;
c.next = &d;
d.next = NULL;
while(head){
//head의 뜻=> "만약 head값이 NULL이 아닐경우 계속 반복"
printf("%d\\n", head->data);
head = head -> next; //head를 다음 포인터의 주소를 저장하는 next값으로 바꿈
}
}
일반적으로 구조체 크기는 멤버 변수들의 크기에 따라 결정된다. 그러나 구조체의 크기가 언제나 멤버 변수들의 크기 총합과 일치하는 것은 아니다.
why? 구조체안 변수들은 연속된 공간을 가지는데 구조체를 메모리에 할당할 때 컴파일러는 프로그램의 속도 향상을 위해 바이트 패딩(byte padding)이라는 규칙을 사용하기 때문에
바이트 패딩 : 메모리의 접근을 쉽게 하기 위해 크기가 가장 큰 멤버 변수를 기준으로 모든 멤버 변수의 메모리 크기를 맞춘다. 이것을 바이트 패딩이라고 하며, 이때 추가되는 바이트를 패딩 바이트(padding byte)라 한다.
#include <stdio.h>
typedef struct
{
char a;
int b;
double c;
}TYPESIZE;
int main(){
puts("구조체 TypeSize의 각 멤버 크기는 다음과 같습니다.");
printf("%d %d %d\\n", sizeof(char), sizeof(int), sizeof(double));
puts("구조체 TypeSize의 크기는 다음과 같습니다.");
printf("%d\\n", sizeof(TYPESIZE));
return 0;
}
double형 타입의 크기가 가장 크므로 8byte가 기준이 된다.
맨처음 char 형 멤버 변수가 8byte에 할당되고 int형 멤버 변수가 할당된다.
마지막 double형 멤버 변수는 8byte인데 남은 공간은 3byte이므로 다시 8byte가 할당 된다.
1byte와 4byte가 할당되고 남은 3byte가 패딩 바이트(padding byte)가 된다.