본문 바로가기
C

구조체

by oncerun 2023. 5. 1.
반응형

 

구조체란?

 

서로 다른 자료형을 갖는 자료들의 모임을 하나의 자료형으로 정의하여 사용하는 자료형이다.

 

배열은 동일 자료형만 다루는 것과 달리 다양한 타입들의 모음을 갖도록 할 수 있다.

 

형태는 다음과 같다.

 

struct 구조체명{

	멤버 1;
    멤버 2;
    
    ...
};

 

예약어인 struct를 사용하여 구조체를 정의하며 이후 하위 구조체를 작성한다. 

 

이렇게 만든 구조체는 다음과 같이 선언할 수 있다. 

 

struct score, var1, var2;

 

실제 자료의 형태를 만들고 이를 구조체로 변환해 보자.

 

 

학번, 이름, 국영수에 대한 점수를 가진 자료를 구조체로 변환해 보자.

 

struct student x,y;


struct student {
    char name[20];
    char no[4];
    int age;
    int score;
};



struct student {
    char name[20];
    char no[4];
    int age;
    int score;
} x, y;  //x,y는 struct student의 구조체 변수이다.



typedef struct student{
    char name[20];
    char no[4];
    int age;
    int score;

} score; // struct student를 score라는 새로운 자료형으로 정의

score x,y; //새로운 자료형 score의 변수 x,y 선언

 

이제 이를 활용하는 법을 알아보자.

 

초기화

 

 struct student s1 = { "홍길동", "201", 20, 90 };
 
 
 혹은
 
 
 struct student {
    char name[20];
    char no[4];
    int age;
    int score;
} s1 = { "홍길동", "201", 20, 90 };

 

구조체 변수를 바로 표현을 하고 초기화도 가능하다.

 

참조

 

구조체 변수명의 도트(.)를 통해 멤버에 접근할 수 있다. 

 

#include <strings.h>

printf("이름: %s\n", s1.name);
printf("학번: %s\n", s1.no);
printf("나이: %d\n", s1.age);
printf("점수: %d\n", s1.score);


struct student s2;
strcpy(s2.name, "김길동");
strcpy(s2.no, "202");
s2.age = 21;
s2.score = 80;

 

 

구조.

 

 

 

sizeof를 통해 구조체의 크기를 알아보면 멤버들의 byte의 합한 값이랑 다르다. 

 

실제로 메모리가 할당될 때는 구조체 멤버 중 가장 큰 자료형의 크기로 할당이 됩니다. 

 

다만 구조체 멤버 자체는 크기가 해당 타입만큼 크기를 갖지만 구조체의 총크기는 가장 큰 자료형 기준으로 채워지는 것입니다.

 

배열 크기가 가장 크다고 처리하는 것이 아닌 순수 자료형의 크기만으로 적용되는 것 같습니다.

 

 

이 구조체를 가지고 배열을 만들면 동일한 구조를 갖는 구조체 변수가 여러 개 사용되는 경우 그 구조체 변수들 담은 배열을 만들어 사용할 수 있습니다.

 

struct student {
    char name[20];
    char no[4];
    int age;
    int score;
} students[100];


혹은
struct student {
    char name[20];
    char no[4];
    int age;
    int score;
};

struct student students[3];

 

 

이를 즉시 초기화하려면 다음과 같이 만들어 줘야 한다.

 

    struct student students[3] = {
            {
                    "홍길동", "201", 20, 90
            },
            {
                    "김길동", "202", 21, 80
            },
            {
                    "이길동", "203", 22, 70
            }
    };

마치 2차원 배열처럼 구성되어 있다.

 

 

구조체 포인터

 

구조체를 다룰 때 포인터로 다룬다면? 

 

구조체를 보다 쉽게 다를 수 있다. 

구조체변수 선언 시 *를 붙여 포인터로 선언하고 구조체 포인터는 포인터와 동일하게 주소값을 갖게 되며 자료가 있는 곳을 가리킨다.

 

 

struct student *ps;

 

참조할 때는 도트 연산자를 사용하고 여기에는 가로가 필요하다. 우선순위 연산자가 필요함. 혹은 화살표를 사용하면 된다.

    (*ps).age = 20;
    ps->age = 20;

가독성 부분에 있어 화살표를 더 많이 사용하는 것 같다.

 

배열과 동일하게 각 구조체의 시작 주소는 구조체 배열 변수명과 같고 그 외 연산은 전부 배열과 동일하다. 

 

 

구조체 개념, 정의, 변수 선언, 초기화 및 참조, 구조체 배열, 구조체 포인터 등을 배웠다. 

 

함수와 구조체

 

 

1. 구조체를 함수의 매개변수로 사용.

일반변수를 함수의 매개변수로 사용하는 것과 동일하다. 

 

매개변수가 구조체인 경우 함수의 형식매개변수를 구조체로 선언해야 한다.

 

그리고 해당 구조체 전체가 복사되기 때문에 편리하다. 다만 구조체 전체가 복사되기 때문에 시간이 많이 걸리고, 기억공간의 낭비가 심하다. 

 

예제를 보자.

 

우선 다음과 같이 함수의 원형선언과 구조체를 선언한다.

struct rectangle {
    int height;
    int width;
};
struct rectangle scale(struct rectangle, int scale);

 

그리고 함수를 정의한다.

struct rectangle scale(struct rectangle r, int scale) {
    struct rectangle result;
    result.height = r.height * scale;
    result.width = r.width * scale;
    return result;
}

 

그리고 main 함수 내에 코드 작성한다.

int main() {

    struct rectangle r1 = { 10, 20 };

    struct rectangle result = scale(r1, 2);

    printf("height: %d, width: %d\n", result.height, result.width);



    return 0;
}

 

타 언어에 비슷한 구조를 가지고 있어 이 부분은 이해하는데 크게 어렵지 않았다.

 

 

구조체를 넘기는 경우 값에 의한 전달을 의미한다. 즉 실제 원본 구조체의 값을 함수 내부에서 변경하였다 해도 이는 복사한 이후 진행하기에 원본 구조체의 메모리 주소 값의 변경을 의미하지는 않는다.

 

struct rectangle {
    int height;
    int width;
};
//struct rectangle scale(struct rectangle, int scale);
void scale(struct rectangle, int scale);



int main() {

    struct rectangle r1 = { 10, 20 };
    printf("height: %d, width: %d\n", r1.height, r1.width);
    scale(r1, 2);

    printf("height: %d, width: %d\n", r1.height, r1.width);


    return 0;
}


void scale(struct rectangle r, int scale) {
    struct rectangle result;
    result.height = r.height * scale;
    result.width = r.width * scale;
}

 

다음에 알아볼 실제 구조체 포인터를 넘긴다는 것은 참조에 의한 전달이다. 

 

 

2. 구조체 포인터를 함수의 매개변수로 사용.

 

구조체를 복사하지 않는다. 주소를 전달하기 때문에 속도가 1번 방법에 비해 속도가 빠르고 기억공간도 덜 사용하게 되어 효율이 좋다. 

 

이 경우 예제를 다음과 같이 변경할 수 있다.

 

struct rectangle {
    int height;
    int width;
};
void scale(struct rectangle *, int scale);



int main() {

    struct rectangle r1 = { 10, 20 };
    printf("height: %d, width: %d\n", r1.height, r1.width);
    scale(&r1, 2);

    printf("height: %d, width: %d\n", r1.height, r1.width);


    return 0;
}


void scale(struct rectangle *r, int scale) {
    r->height *= scale;
    r->width *= scale;
}

 

 

typedef

 

typedef 예약어는 이미 존재하는 자료형에 새로운 이름을 붙이기 위한 키워드이다.

 

typedef unsigned int BYTE;

 

혹은

 

구조체를 다음과 같이 재정의할 수 도 있다.

struct data{
    int x;
    int y;
};

typedef struct data DATA;

 

이를 더 간결하게 표현하면 다음과 같다.

 

typedef struct data{
    int x;
    int y;
} DATA;

 

bit field

 

주기억장치의 기억공간을 byte 단위가 아닌 bit 단위로 사용. 

 

저장공간을 매우 효율적으로 사용하기 위해 사용된다. 

 

struct nibble{
    unsigned low : 1;
    unsigned high : 4;
};

 

비트필드의 자료형은 int나 unsigned 로 선언되어야 한다. 그리고 포인터나 배열은 사용되지 않고 비트필드의 전체 크기는 시스템이 제공하는 int의 크기 이내여야 한다.

 

 

 

공용체 ( union)

 

동일한 기억장소에 여러 유형의 자료를 저장하기 위해서 프로그래머가 선언한 자료형이다.

 

공용체 안에 포함된 자료들이 같은 기억장소를 공유하여 사용한다. 

 

사용될 자료의 자료형이 유동적일 경우 기억 공간을 효율적으로 사용할 수 있는 장점이 있다. 

 

공용체의 멤버들이 완전히 다른 자료형을 가질 때 기억공간을 절약하기 위해 사용된다고 한다.

 

예를 들어 원화를 표현하는 경우와 달러를 표현하는 경우 정수형과 실수형의 차이가 있을 수 있다.

 

이러한 경우 공용체를 사용하면 필요에 따라 메모리의 자료형을 선택해서 값을 저장할 수 있다.

 

다음과 같이 정의할 수 있다.

union number {
    int x;
    float y;
};

 

 

공용체가 사용되면 공용체 멤버 중에서 자료크기가 가장 큰 멤버에 대해서만 기억공간이 할당되고 기억 공간의 시작 위치부터 각 부분을 다른 멤버가 공용으로 사용한다.

 

 

 

 

반응형

'C' 카테고리의 다른 글

메모리 동적 할당  (0) 2023.05.02
포인터와 배열.  (0) 2023.04.29
포인터  (0) 2023.04.29
C언어에서 배열을 어떻게 다룰까?  (0) 2023.04.17
입력 및 출력 프로그램  (0) 2023.04.16

댓글