[C] 구조체 2
구조체에 포인터를 활용하기
#include <stdio.h>
#pragma warning (disable : 4996)
typedef struct score {
int kor, eng, mat;
}score;
int main () {
score s = {70, 80, 90}; // kor에 70, eng에 80, mat에 90 으로
// 변수를 선언한 순서대로 초기값이 선언된다.
// 만약에 s = {70}; 만을 선언한다면
// eng와 mat에는 0이 선언된다.
score* p;
// score 타입의 주소를 저장하는 포인터 변수.
// 크기랑은 상관없이 64비트 기준 8바이트이다.
p = &s;
// s가 score 타입이고, p는 그를 저장하는 포인터 변수이므로
// 위와 같은 코드 작성이 가능하다.
// 이 때, 주소가 가리키는 값으로 출력할 경우
// ->을 사용한다.
printf("%d %d %d\n", s.kor, s.eng, s.mat);
printf("%d %d %d\n", p->kor, p->eng, p->mat);
printf("%d %d %d\n", (*p).kor, (*p).eng, (*p).mat);
printf("%d %d %d\n", p[0].kor, p[0].eng, p[0].mat);
// 넷의 결과는 완벽하게 같다.
// 이 중에서 화살표를 쓰는 것이 조금 더 권장된다.
}
위의 코드에서
- 변수에 대해서 접근할 때에는 .을 사용한다.
- 포인터에 대해서 접근할 때에는 ->을 사용한다.
- 구조체를 가리키는 포인터의 크기 또한 크기는 8바이트이다.
(64비트 기준)
라는 것을 관찰할 수 있다.
따라서 코드에도 언급되어 있지만 printf 4개에서 나타난 결과는 완벽하게 동일하다.
그리고 잊지말자. p[0]와 *p는 완벽하게 같다.
한 배열에 구조체를 저장하기
typedef struct score {
int kor, eng, mat;
}score;
int main () {
score s[3] = { {70, 80, 90}, {60}, {90, 80} };
}
score 타입 3개를 한 배열에 선언하는 것도 가능하다. 즉, score 타입 3개가 배열에 저장된 것이다.
s[0] = {70, 80, 90}
s[1] = {60, 0, 0}
s[2] = {90, 80, 0}
이런 상태이다.
여기서, score s[3] = {70, 80, 90, 60, 90, 80}; 이렇게 선언한다면 s[0]부터 순서대로 채운다음에 s[1]의 값을 채워나가게 된다. 즉
s[0] = {70, 80, 90}
s[1] = {60, 90, 80}
s[2] = {0, 0, 0}
따라서, 위 코드에 이어서 아래의 코드를 붙여준다면
score* p;
// score 타입의 주소를 저장하는 포인터 변수.
// 크기랑은 상관없이 64비트 기준 8바이트이다.
p = s; // s == &s[0]
printf("%d %d %d\n", s[0].kor, s[0].eng, s[0].mat);
printf("%d %d %d\n", (p+0)->kor, (p+0)->eng, (p+0)->mat);
printf("%d %d %d\n", (p+1)->kor, (p+1)->eng, (p+1)->mat);
printf("%d %d %d\n", (p+2)->kor, (p+2)->eng, (p+2)->mat);
printf("%d %d %d\n", (*p).kor, (*p).eng, (*p).mat);
printf("%d %d %d\n", p[1].kor, p[1].eng, p[1].mat);
이것의 출력 결과는 아래와 같이 나온다.
// printf("%d %d %d\n", s[0].kor, s[0].eng, s[0].mat);
70 80 90
/*
printf("%d %d %d\n", (p+0)->kor, (p+0)->eng, (p+0)->mat);
printf("%d %d %d\n", (p+1)->kor, (p+1)->eng, (p+1)->mat);
printf("%d %d %d\n", (p+2)->kor, (p+2)->eng, (p+2)->mat);
*/
70 80 90
60 0 0
90 80 0
/*
printf("%d %d %d\n", (*p).kor, (*p).eng, (*p).mat);
printf("%d %d %d\n", p[1].kor, p[1].eng, p[1].mat);
*/
70 80 90
60 0 0
s[0].kor과 (p+0)->kor에 대한 값이 동일함을 다시한번 확인할 수 있다.
사용자 정의 함수에서 구조체 호출하기
구조체와 포인터는 사용자 정의 함수에서 진가를 발휘한다.
사용자 정의 함수를 사용하지 않을 경우 코드
#include <stdio.h>
#include <stdlib.h> //system
#include <string.h>
#define BOOK_NAME_LEN 40
#define AUTHOR_NAME_LEN 20
#define BOOK_KIND_NUM 3 // 도서의 종류 수
typedef struct book
{
int no;
char name[BOOK_NAME_LEN];
char author[AUTHOR_NAME_LEN];
int price;
int num;
}book;
void remove_enter(int len, char* in) {
// fgets 사용으로인해 입력된 \n을 \0으로 변환하기 위한 코드
in[len - 1] = '\0';
}
int main() {
//구조체 변수 선언
book s[BOOK_KIND_NUM]; //구조체 배열
//도서정보 입력
for(int i = 0; i < BOOK_KIND_NUM; i++)
{
system("clear"); //화면지우기
printf("\n\n\t\t***도서정보 입력***\n\n");
s[i].no = 1111 + i;
printf("일련번호 : [%03d]\n", s[i].no);
printf("도서 이름 : ");
fgets(s[i].name, BOOK_NAME_LEN - 1, stdin);
remove_enter(strlen(s[i].name), s[i].name);
printf("저자명 : ");
fgets(s[i].author, AUTHOR_NAME_LEN - 1, stdin);
remove_enter(strlen(s[i].author), s[i].author);
printf("가격 : ");
scanf("%d", &s[i].price);
while (getchar() != '\n'); // scanf가 buffer가 \n을 가지고 있으면 넘어가는 것을 방지.
printf("재고 : ");
scanf("%d", &s[i].num);
while (getchar() != '\n'); // scanf가 buffer가 \n을 가지고 있으면 넘어가는 것을 방지.
}
//도서정보 출력
system("clear");
printf("\n\n\t*** 도서정보 출력 ***\n\n");
printf("%4s %-40s %-20s %8s %8s %8s\n", "일련번호", "도서명", "저자명", "가격", "재고", "매출");
printf("----------------------------------------------------------------------------------\n");
for(int i = 0; i < BOOK_KIND_NUM; i++)
{
printf("%4d %-40s %-20s %8d %8d %8d\n", s[i].no, s[i].name, s[i].author, s[i].price, s[i].num, (s[i].num * s[i].price));
}
}
사용자 정의 함수를 사용할 경우 코드
#include <stdio.h>
#include <stdlib.h> //system
#include <string.h>
#define BOOK_NAME_LEN 40
#define AUTHOR_NAME_LEN 20
#define BOOK_KIND_NUM 3 // 도서의 종류 수
typedef struct book
{
int no;
char name[BOOK_NAME_LEN];
char author[AUTHOR_NAME_LEN];
int price, num;
}book;
void remove_enter(int len, char* in) {
// fgets 사용으로인해 입력된 \n을 \0으로 변환하기 위한 코드
in[len - 1] = '\0';
}
void input(book* s) { // book타입에 대한 변수의 주소를 받아야한다.
// int*, char* 하듯이 똑같다.
for(int i = 0; i < BOOK_KIND_NUM; i++)
{
system("clear"); //화면지우기
printf("\n\n\t\t***도서정보 입력***\n\n");
s[i].no = 1111 + i;
// s[i].no는 (s + i)->no와 같다.
// 대신에 섞어쓰지만 말자.
printf("일련번호 : [%03d]\n", s[i].no);
printf("도서 이름 : ");
fgets(s[i].name, BOOK_NAME_LEN - 1, stdin);
remove_enter(strlen(s[i].name), s[i].name);
printf("저자명 : ");
fgets(s[i].author, AUTHOR_NAME_LEN - 1, stdin);
remove_enter(strlen(s[i].author), s[i].author);
printf("가격 : ");
scanf("%d", &s[i].price);
while (getchar() != '\n'); // scanf가 buffer가 \n을 가지고 있으면 넘어가는 것을 방지.
printf("재고 : ");
scanf("%d", &s[i].num);
while (getchar() != '\n'); // scanf가 buffer가 \n을 가지고 있으면 넘어가는 것을 방지.
}
}
int main() {
//구조체 변수 선언
book s[BOOK_KIND_NUM]; //구조체 배열
//도서정보 입력
input(s); // 이때, book타입인 s에 대한 주소를 넘긴다.
//도서정보 출력
system("clear");
printf("\n\n\t*** 도서정보 출력 ***\n\n");
printf("%4s %-40s %-20s %8s %8s %8s\n", "일련번호", "도서명", "저자명", "가격", "재고", "매출");
printf("----------------------------------------------------------------------------------\n");
for(int i = 0; i < BOOK_KIND_NUM; i++)
{
printf("%4d %-40s %-20s %8d %8d %8d\n", s[i].no, s[i].name, s[i].author, s[i].price, s[i].num, (s[i].num * s[i].price));
}
}
// 출력 결과 예시
일련번호 도서명 저자명 가격 재고 매출
----------------------------------------------------------------------------------
1111 우키 우키킴 20000 5 100000
1112 라라 라라킴 10000 10 100000
1113 루루 냐냐루 23242 56 1301552
터미널에서 출력하느라 살짝 찌그러졌지만 넘어가면
두 코드의 차이는 input 부분을 사용자 정의 함수로 넘겼냐의 차이이다.
main에서 input하는 부분을 사용자 정의 함수로 넘기는 데 사용할 수 있고, 결과는 동일하게 출력이 된다.