구조체 변수의 주소는 구조체 포인터에 저장하며 구조체 변수 전체를 가리킨다. (구조체 멤버 X)
☞ 구조체 변수의 주소를 구하면 멤버의 주소가 아니라 구조체 변수 전체의 주소가 구해진다.
# include <stdio.h>
struct score
{
int kor; // 국어 점수
int eng; // 영어 점수
int math; // 수학 점수
};
int main()
{
struct score yuni = { 90, 80, 70 }; // 구조체 변수 선언과 초기화
struct score *ps = &yuni; // 구조체 포인터에 주소 저장
printf("국어 : %d\n", (*ps).kor); // 구조체 포인터로 멤버 접근
printf("영어 : %d\n", ps -> eng); // -> 연산자 사용
printf("수학 : %d\n", ps -> math);
}
-> 연산자를 사용하여 구조체의 멤버에 쉽게 접근할 수 있다.
(*ps).kor → ps -> kor
(*ps).eng → ps -> eng
(*ps).math → ps -> math
같은 형태의 구조체 변수가 많이 필요하면 배열로 선언하여 사용할 수 있다.
#include <stdio.h>
struct address
{
char name[20]; // 이름
int age; // 나이
char tel[20]; // 전화번호
char addr[80]; // 주소
};
int main()
{
struct address list[5] = { // 요소가 5개인 구조체 배열 선언
{"홍길동", 23, "111-1111", "울릉도 독도"},
{"이순신", 35, "222-2222", "서울 건천동"},
{"장보고", 19, "333-3333", "완도 청해진"},
{"유관순", 15, "444-4444", "충남 천안"},
{"안중근", 45, "555-5555", "황해도 해주"}
};
int i;
// 배열 요소 수만큼 반복
for(i = 0; i < 5; i++)
{
printf("%10s %5d %15s %20s\n",
list[i].name, list[i].age, list[i].tel, list[i].addr);
}
}
▼ 출력부분을 함수로 만들어 구조체 포인터로 다루기
#include <stdio.h>
struct address
{
char name[20]; // 이름
int age; // 나이
char tel[20]; // 전화번호
char addr[80]; // 주소
};
// 각 배열 요소의 멤버 출력 함수
void print_list(struct address *lp)
{
int i;
// 배열 요소 수만큼 반복
for(i = 0; i < 5; i++)
{
printf("%10s %5d %15s %20s\n",
(lp+i)->name, (lp+i)->age, (lp+i)->tel, (lp+i)->addr);
}
}
int main()
{
struct address list[5] = { // 요소가 5개인 구조체 배열 선언
{"홍길동", 23, "111-1111", "울릉도 독도"},
{"이순신", 35, "222-2222", "서울 건천동"},
{"장보고", 19, "333-3333", "완도 청해진"},
{"유관순", 15, "444-4444", "충남 천안"},
{"안중근", 45, "555-5555", "황해도 해주"}
};
print_list(list);
}
함수의 매개변수는 구조체 포인터로 구조체 배열의 시작 주소를 가리키게 된다.
배열 표현 → lp[i].name
포인터 표현 → (*(lp+i)).name
->연산자 사용 → (lp+i)->name
자기 참조 구조체는 자신의 구조체를 가리키는 포인터를 멤버로 가진다.
▼ 연결 리스트 예제
#include <stdio.h>
struct list
{
int num; // 데이터
struct list *next; // 구조체 자신을 가리키는 포인터 멤버
};
int main()
{
struct list a = {10, 0}, b = {20, 0}, c = {30, 0}; // 구조체 변수 초기화
struct list *head = &a, *current; // 헤드 포인터 초기화
a.next = &b; // a의 포인터 멤버가 b를 가리킴
b.next = &c; // b의 포인터 멤버가 c를 가리킴
printf("head->num : %d\n", head->num); // head가 가리키는 a의 num 멤버 사용
printf("head->next->num : %d\n", head->next->num); // head로 b의 num 멤버 사용
printf("list all : ");
current = head; // 최초 current 포인터가 a를 가리킴
while (current != NULL) // 마지막 구조체 변수까지 출력하면 반복 종료
{
printf("%d ", current->num; // current가 가리키는 구조체 변수의 num 출력
current = current->next; // current가 다음 구조체 변수를 가리키도록 함
}
printf("\n");
}
공용체는 구조체와 선언 방식이 비슷하지만 모든 멤버가 하나의 저장 공간을 같이 사용한다.
#include <stdio.h>
union student
{
int num; // 학번
double grade; // 학점
};
int main()
{
union student s1 = { 315 }; // 공용체 변수 선언과 초기화
printf("학번 : %d\n", s1.num); // 학번 멤버 출력
s1.grad = 4.4; // 학점 멤버에 값 대입
printf("학점 : %.1lf\n", s1.grade);
printf("학번 : %d\n", s1.num); // 학번 다시 출력
}
실행 결과 학번을 다시 출력했을 때 이상한 값이 출력된다.
// 첫 번째 멤버가 아닌 멤버를 초기화할 때
union student a = { .grade = 4.3 }; // grade 멤버를 4.3으로 초기화
// 첫 번째 멤버만 초기화
union student a1 = { 315 };
여러 멤버가 하나의 저장 공간을 공유하므로 메모리를 절약할 수 있고 같은 공간에 저장된 값을 여러가지 형태로 사용할 수 있는 장점이 있다.
#include <stdio.h>
enum season {SPRING, SUMMER, FALL, WINTER}; // 열거형 선언
int main()
{
enum season ss; // 열거형 변수 선언
char *pc = NULL; // 문자열을 저장할 포인터
ss = SPRING; // 열거 멤버의 값 대입
switch(ss) // 열거 멤버 판단
{
case SPRING: // 봄이면
pc = "inline"; // 인라인 문자열 선택
break;
case SUMMER: // 여름이면
pc = "swimming"; // 수영 문자열 선택
break;
case FALL: // 가을이면
pc = "trip"; // 여행 문자열 선택
break;
case WINTER: // 겨울이면
pc = "skiing"; // 스키 문자열 선택
break;
}
printf("나의 레저 활동 => %s\n", pc); // 선택된 문자열 출력
}
컴파일러는 멤버를 0부터 차례로 하나씩 큰 정수로 바꾼다.
☞ SPRING = 0 / SUMMER = 1 / FALL = 2 / WINTER = 3
원하는 값으로 설정할 수도 있다.
enum season {SPRING = 5, SUMMER, FALL = 10, WINTER};
이때 SUMMER와 WINTER은 이전 값의 하나 큰 정수로 바뀐다.
typedef를 사용하면 자료형 이름에서 struct 같은 예약어를 생략할 수 있다.
#include <stdio.h>
struct student
{
int num;
double grade;
};
typedef struct student Student; // Student형으로 재정의
void print_data(Student *ps) // 매개변수는 Student형의 포인터
{
printf("학번 : %d\n", ps -> num); // Student 포인터로 멤버 접근
printf("학점 : %.1lf\n", ps -> grade);
}
int main()
{
Student s1 = { 315, 4.2 };
print_data(&s1);
}
재정의 하기 전의 자료형을 굳이 사용할 필요가 없으면 형 선언과 동시에 재정의할 수 있다.
typedef struct // 재정의될 것이므로 구조체 이름 생략
{
int num;
double grade;
} Student; // 재정의된 자료형 이름