[★C] 구조체 (1)

방법이있지·2025년 6월 10일
post-thumbnail

구조체를 이용하면 서로 다른 자료형의 데이터를 하나로 묶어 저장, 관리할 수 있습니다. 역시나 C언어답게 구조체만을 가리키는 포인터도 있습니다.

구조체

  • 여러 가지 자료형을 하나로 묶어 관리할 수 있는 복합 자료형
    • 배열: 같은 자료형의 데이터만 묶어 저장할 수 있음
    • 구조체: 서로 다른 자료형의 데이터를 하나로 묶어 저장 가능

구조체 선언

#include <stdio.h>
#include <string.h>

struct profile{
    char name[20];      // 이름
    int height;         // 키
    double weight;      // 몸무게
    char *intro;        // 자기소개
};
  • struct 구조체명으로 구조체를 선언
    • 이 과정에서 struct 구조체명이라는 새로운 자료형이 생성된다고 이해하면 됨
  • 이후 중괄호 안에, 구조체에 포함되는 변수(멤버)의 자료형과 이름 나열
    • 멤버에는 배열, 포인터, 정수, 실수, 다른 구조체 등 포함 가능
  • 위 코드에서는 프로그램 전체에서 사용할 수 있게, main 함수 밖에 구조체를 선언함
// 위 코드에서 계속
int main(void){
  struct profile sangrok;
  strcpy(sangrok.name, "우리상록이");
  sangrok.height = 184;
  sangrok.weight = 69.7;
  sangrok.intro = "안녕 나는 상록이야~";

  struct profile pika = {"피카츄", 40, 6.0, "피카피카!"};

  printf("%s (%dcm, %.1lfkg): %s\n", sangrok.name, sangrok.height, sangrok.weight, sangrok.intro);
  printf("%s (%dcm, %.1lfkg): %s\n", pika.name, pika.height, pika.weight, pika.intro);

  // [출력]
  // 우리상록이 (184cm, 69.7kg): 안녕 나는 상록이야~
  // 피카츄 (40cm, 6.0kg): 피카피카!
}
  • struct 구조체명 구조체_변수명으로 변수 선언
    • struct profile: 앞서 만든 struct profile형의 변수 p1 선언
    • 값을 개별적으로 대입해도 되고, 중괄호로 각 값에 맞게 초기화도 가능
  • 구조체_변수명.멤버명으로 각 멤버에 접근
    • s1.height, s2.weight 등을 이용해, 일반 변수처럼 값을 대입, 출력할 수 있음

구조체 변수의 크기

  • 각 자료형은 주소를 KK의 배수로 가져야 함
    • KK는 자료형의 크기 (바이트 단위)
    • CPU가 KK바이트 데이터를 KK의 배수인 주소에서 효율적으로 접근하도록 설계되어 있기 때문
    • e.g., int형의 주소는 4의 배수, double형의 주소 는 8의 배수여야 함
  • 구조체의 전체 크기는 가장 큰 멤버 크기의 배수여야 함
  • 바이트 정렬: 위 규칙이 지켜지지 않을 시, 데이터 사이에 빈 공간인 패딩 바이트를 삽입해 정렬을 맞춤
struct student{
  char ch1;       // K = 1
  short num;      // K = 2
  char ch2;       // K = 1
  int score;      // K = 4
  double grade;   // K = 8
  char ch3;       // K = 1
};
struct student s1;
printf("구조체의 크기: %d바이트", sizeof(s1));
// [출력] 구조체의 크기: 32바이트

  • 각 멤버의 크기의 단순합은 17바이트지만, 바이트 정렬과 패딩으로 인해 실제 크기는 32바이트
    • 구조체는 제일 큰 멤버인 double형의 크기, 즉 8의 배수여야 하므로, 맨 뒤에도 7바이트 패딩이 삽입됨

구조체를 멤버로 갖는 구조체

  • 구조체의 멤버로 다른 구조체를 포함할 수 있음
  • e.g., 선수의 성적을 나타내는 구조체 stats를, 선수 정보를 담는 구조체 player의 멤버로 포함 가능
    • 단, 멤버로 포함될 구조체 statsplayer보다 먼저 선언되어야 함
#include <stdio.h>

// 성적 구조체 선언
struct stats {
    int so;             // 탈삼진
    double era;         // 평균자책점
};

// 선수 구조체 선언
struct player{
    struct stats st;    // stats 구조체를 멤버로 사용
    char *name;         // 이름
    char *team;         // 소속팀
    int number;         // 등번호
};

int main(void){
    struct player p1;
    p1.st.so = 67;
    p1.st.era = 2.30;
    p1.name = "송승기";
    p1.team = "LG 트윈스";
    p1.number = 13;

    // 구조체를 멤버로 갖는 경우, 중괄호 안에 중괄호를 넣어 초기화 가능
    struct player p2 = {{23, 3.91}, "박명근", "LG 트윈스", 39};

    printf("%s No. %d %s: 탈삼진 %d개, 평균자책점 %.2lf\n", p1.team, p1.number, p1.name, p1.st.so, p1.st.era);
    printf("%s No. %d %s: 탈삼진 %d개, 평균자책점 %.2lf\n", p2.team, p2.number, p2.name, p2.st.so, p2.st.era);

    // [출력]
    // LG 트윈스 No. 13 송승기: 탈삼진 67개, 평균자책점 2.30
    // LG 트윈스 No. 39 박명근: 탈삼진 23개, 평균자책점 3.91

    return 0;
}
  • p1.st.era -> p1 구조체 변수의 st 멤버에 접근하고, st 구조체 변수의 era 멤버에 접근
  • 구조체 내 구조체를 초기화할 때는 중첩 중괄호를 사용

함수 매개변수로 구조체 전달하기

  • 함수는 매개변수로 구조체를 받을 수 있고, 구조체를 반환할 수 있음
  • 제 최애 음식인 만두의 가격을 1500원 할인하는 함수를 만들어보겠습니다.
#include <stdio.h>

struct food {
    char *name;
    int price;
};

// 구조체를 매개변수로 받고, 구조체를 반환
struct food sale(struct food f);

int main(void){
    struct food mandu = {"고기만두", 7000};
    // sale 함수의 반환값으로 mandu를 갱신
    mandu = sale(mandu);
    printf("맛있는 %s가 %d원입니다!", mandu.name, mandu.price);
    // 맛있는 고기만두가 5500원입니다!
}

// 전달받은 구조체 f의 price에서 1500을 할인
struct food sale(struct food f){
    f.price -= 1500;
    return f;
}
  • sale 함수가 받은 구조체 f는, main 함수의 mandu의 멤버의 값을 복사한 복사본
    • f.price의 값을 바꿔도 원본 mandu.price는 바뀌지 않음
    • 따라서 변경된 구조체 f를 반환하고, 결과를 mandu에 대입해 주어야 함

구조체 포인터와 -> 연산자

  • 구조체 변수의 주소는 구조체 포인터에 저장할 수 있음
  • 이때 포인터는 구조체 변수 전체를 가리킴
#include <stdio.h>

struct price {
    int mandu;      // 만두의 가격
    int soondae;    // 순대의 가격
    int hotteok;    // 호떡의 가격
};

int main(void){
    struct price p1 = {5000, 6000, 3000};   // 구조체 변수 선언, 초기화
    struct price *pp = &p1;                 // 구조체 포인터에 주소 저장
    printf("만두 가격: %d\n", (*pp).mandu);
    printf("순대 가격: %d\n", pp -> soondae);
    printf("호떡 가격: %d\n", pp -> hotteok);

    // [출력]
    // 만두 가격: 5000
    // 순대 가격: 6000
    // 호떡 가격: 3000
    return 0;
}
  • struct price *형 포인터 pp는 구조체 변수 p1을 가리킴
  • (*pp).mandu: pp가 가리키는 p1을 간접 참조하여, 멤버 mandu에 접근
  • pp -> soondae처럼 ->를 사용해도, 포인터가 가리키는 구조체 변수의 멤버에 접근 가능
    • pp -> soondae(*pp).soondae와 같은 기능 수행
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

0개의 댓글