CS 지식 정리

구조체란? - C언어 알아보기

termuni 2025. 2. 25. 10:07

아무래도 임베디드 SW를 하길 원한다는 사람이 C++을 사용해서 준비하는 것 보다, C를 사용해서 준비하는 것이 훨 좋을 것이라 생각되었다.

 

그래서 C언어 기반 자료구조부터 알고리즘까지 배우는 책을 샀고, 그 책을 기반으로 공부하려고 한다.

 

근데, 챕터 1~2 사이는.. 배열, 반복 이런걸 다루고 있어서, 과감히 스킵.

 

조~금 생소할 수 있는 구조체에 대해 시작하고, 이후 검색 알고리즘을 정리하겠다.


기본적으로 구조체란 데이터를 한데 묶어서 처리하기 편하게 만드는 자료구조이다. 

 

만약 이름, 키, 시력을 저장해야하는데, 엑셀에서 다음과 같이 저장했다고 생각해보자.

 

이렇게 데이터를 누가 저장해 두었다면.. 한숨부터 나오지 않을까? 싶다.

 

그렇기에 이렇게 잘 정돈된 데이터를 만들 필요가 있다. 그런데, 배열만 이용해서는 위와 같이 만들기 어렵다. 왜냐하면 배열을 쓰면 다음과 같이 정리 될 것이기 때문.

 

그렇기에, 구조체를 써서 아예 확실하게 잡아두어야 알아보기 편할 것이다!

 

이것이 구조체를 쓰는 이유라고 보면 될 것 같다.


 

typedef struct{
    char name[20];
    int height;
    double vision;
} PhysCheck;

 

구조체 선언의 예시로, 위와 같이 선언할 수 있다.

 

typedef struct를 쓰지 않고, struct PhysCheck { ... }; 로도 쓸 수 있지만, 그렇게 하는 경우 해당 형식을 부를 때 마다 매우 귀찮아진다.

struct PhysCheck dat; 이라고 선언해야하고, 이를 가르키는 포인터 또한 struct PhysCheck *p = &dat; 과 같이, 귀찮아 지므로 typedef 를 쓰는 것을 권장한다.

 

만약 PhysCheck를 선언했고, 이 내부의 인자를 접근하려면 다음과 같이 접근한다.

    PhysCheck dat[] = {
        {"박현규", 162, 0.3},
        {"함진아", 173, 0.3},
        {"최윤미", 175, 2.0},
        {"홍연의", 171, 1.5},
        {"이수진", 168, 0.4},
        {"김영준", 174, 1.2},
        {"박용규", 169, 0.8},
    };
    
    dat[i].name, dat[i].height, dat[i].vision

 

 

이를 바탕으로 한 실습 코드는 다음과 같다. 

#define VMAX 21
#include<stdio.h>

typedef struct{
    char name[20];
    int height;
    double vision;
} PhysCheck;

//키 평균값 구하는 함수
double Avg_Height(const PhysCheck dat[], int n)
{
    double sum = 0;
    for(int i=0; i<n; ++i)
    {
        sum += dat[i].height;
    }
    return sum / (double)n;
}

//시력 분포
void Dist_Vision(const PhysCheck dat[], int n, int dist[])
{
    //dist의 분포 초기화
    for(int i=0; i<VMAX; ++i)
    {
        dist[i] = 0;
    }

    for(int i=0; i<n; ++i)
    {
        if(dat[i].vision >= 0.0 && dat[i].vision <= VMAX/10.0)
        {
            dist[(int)(dat[i].vision * 10)]++;
        }
    }
}

int main()
{
    PhysCheck dat[] = {
        {"박현규", 162, 0.3},
        {"함진아", 173, 0.3},
        {"최윤미", 175, 2.0},
        {"홍연의", 171, 1.5},
        {"이수진", 168, 0.4},
        {"김영준", 174, 1.2},
        {"박용규", 169, 0.8},
    };

    int n_dat = sizeof(dat)/sizeof(PhysCheck);
    int vdist[VMAX];
    puts("------신체검사표------");
    puts("\t이름\t키\t시력");
    puts("----------------------");
    for(int i=0; i<n_dat; ++i)
    {
        printf("%-18.18s%4d%5.1f\n", dat[i].name, dat[i].height, dat[i].vision);
    }
    printf("\n 평균 키 : %5.1f cm\n", Avg_Height(dat, n_dat));
    Dist_Vision(dat, n_dat, vdist);
    puts("\n시력 분포");
    for(int i=0; i<VMAX; ++i)
    {
        //printf("%3.1f ~: %2d명\n", i/10.0, vdist[i]);
        printf("%3.1f ~ : ", i/10.0);
        for(int j=0; j<vdist[i];++j)
        {
            printf("*");
        }
        puts("");
    }
    
    return 0;

}

 

여기서 이를 보면서 굳이 dat[]를 써서 배열로 접근하지 않고 포인터로 접근 할 방법은 없을까 생각했는데.. 생각보다 헷갈린다. 

 

애초에 const PhysCheck * dat을 하는 경우, dat 배열에 대해서 이 시작 지점의 주소를 가져오게 된다.

그렇다면 이후의 값을 참조하는 것에 있어 휴먼 에러가 생길 가능성이 있어, 차라리 dat[]으로 가져오는 것이 이해에 쉬울 것이라는 판단이 되었다.