[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하는 부분을 사용자 정의 함수로 넘기는 데 사용할 수 있고, 결과는 동일하게 출력이 된다.


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2