본문 바로가기
Programming/C

[C] 구조체 정리

by 임아톰 2019. 2. 4.
1.구조체를 왜 사용할까? (사용자 지정 자료형)

C언어에서 구조체가 왜 필요한 지 알기 위해서는 먼저 자료형이 무엇인지에 대해  알아볼 필요가 있습니다.

자료형은 일정 길이의 메모리에 저장된 정보를 해석하는 방법입니다. 메모리는 변수를 저장하기 위한 하드웨어이죠. 
 
 단순히 생각하면 메모리에는 숫자 밖에 들어있지 않지만 자료형에 따라 정수가 되기도 하고 실수가 되기도 하고 심지어는 문자가 되기도 합니다. 같은 숫자를 두고 해석하는 방법에 따라 문자가 되기도 하고 숫자가 되기도 합니다. 더도 덜도 아니고 그냥 그렇게 하기로 약속한 것입니다. 아스키 코드에서 숫자 65가 꼭 문자 'A'일 필요는 없으니까 말이죠.
 
 자료형을 그릇에 비유하기도 합니다. 밥 그릇에 밥을 담고 국 그릇에는 국을 담는 것처럼 C언어에서는 정수를 담는 그릇은 int인셈이고 문자를 담는 그릇은 char형인 셈입니다. 
 
 그릇을 매번 하나씩 주문하기 어려우니 한번에 100개씩, 1000개씩 주문하는 방법이 배열입니다. 따라서 배열은 데이터를 자료형 별로 묶어서 관리합니다. 배열은 동일 형식의 연속된 집합체입니다. 1000 그릇을 한번에 주문 할 수 있는 것은 좋으나 아쉬운 점 또한 있는데 이러한 배열은 내용 별로 데이터를 다루기가 쉽지 않다는 점입니다. 
 
 이런 생각을 해볼 수 있습니다. 짜장면과 짬뽕을 기가 막히게 맛있게 만드는 중국집이 있습니다. 손님들은 항상 둘 중 무엇을 먹을까 고민을 하죠. 어느날 중국집 사장님은 손님의 고민을 덜어주기 위해 짬짜면을 팔아야겠다고 생각합니다. 이때 짜장면과 짬뽕을 같이 담을 수 있는 짬짜면 전용 그릇(가운데 칸 막이가 있는)이 없다면 짬짜면 팔기는 매우 불편할것입니다.  짬짜면을 팔기 위해 짬짜면 그릇을 주문 하는 것이 구조체를 사용하는 이유입니다. 필요한 자료형이 있으면 구조체를 정의함으로서 사용자가 새로운 자료형을 지정할 수 있습니다. 한마디로 구조체는 편하기 위해 존재합니다. 게다가 중국집 사장님은 번거롭게 그릇을 주문해야하지만 C언어에서는 그냥 'struct'를 외치면 됩니다.
 
즉, 구조체는 서로 같거나 다른 형식들의 연속된 집합체입니다. 배열과 달리 내용 별로 데이터를 다룰 수 있습니다.
 
예를들어, 구조체를 사용하면 이런게 가능해집니다! 학생과 관련된 데이터(이름, 학번, 학점, 전화번호...)들을 서로 자료형이 다를지라도 내용별로 하나로 묶어서 하나의 변수처럼 사용할 수 있습니다.
 
 
2.구조체 정의

구조체는 미리 어떠한 형태를 모아서 새로운 자료 형태를 만들건지 컴파일러에게 미리 신고를 해줘야 합니다. 자료형에 대한 구조를 먼저 알려주고 나서 그 자료형으로 변수를 선언해줘야 기억 공간에 잡힙니다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma warning(disable:4996)
#include <stdio.h>
struct student
{
    int num;
    double grade;
};
 
int main(void)
{
    struct student a;
    scanf("%d"&a.num);
    scanf("%lf"&a.grade);
    printf("번호: %d 학점: %.1lf", a.num, a.grade);
    return 0;
}
cs

 

 

4-6::

 

 

 

구조체를 구성하는 하나의 멤버, 마치 변수를 선언하는 것처럼되어있지만 변수 선언은 아닙니다. 초기화 또한 불가능합니다. 멤버로 사용하겠다는 신고를 하는 것뿐이지, 변수를 선언하는 명령은 아닙니다.

11::

struct student 전체를 int와 같은 자료형으로 생각하면 편합니다. 즉, struct student형인 a를 선언한 셈입니다. 구조체 변수를 선언하면 비로소 저장 공간이 할당됩니다.
12-13::
구조체에 담기는 각 변수들을 멤버라고 합니다.
이러한 멤버들을 직접 지정해줘야지만 그 공간을 쓸 수가 있습니다. 구조체 변수를 통으로 쓰는 것은 아니고 멤버들을 멤버 접근 연산자를 이용하여 하나씩 사용해야 합니다
멤버연산자는 다음과 같이 사용합니다. [구조체 변수명 . 멤버명] 구조체 변수에 num(멤버명)이라는 멤버를 쓰겠다. 구조체는 배열과 달리 멤버들의 크기가 다 다르므로 일정한 간격으로 할당된 것이 아니므로 반드시 멤버명으로 접근해야됩니다. 반면에 배열은 인덱스로 접근합니다.
 
 * 연산자 . -> ( ) [ ]는 우선순위가 가장 높다.
 
3.패딩 바이트

위의 구조체의 경우, 멤버가 double과 int 이므로 12 바이트의 크기를 가질 것으로 예상됩니다. 하지만 실제 sizeof로 크기를 출력해보면 16바이트가 나옵니다. 이는 패딩 바이트가 들어가기 때문입니다. 시스템은 cpu가 메모리에 접근할 떄 접근 효율을 높이기 위해 일정한 바이트 단위로 접근합니다. 멤버의 크기가 들쑥날쑥한 경우 멤버 사이에 패딩 바이트를 넣어 멤버들을 정렬합니다. (바이트 얼라인먼트, byte alignment

 
패딩 바이트의 경우는 메모리를 너무 많이 잡아먹는 심각한 문제를 발생시킬 수 있습니다. 
 

 

순서만 바껴도 데이터 낭비를 아낄수 있으므로 구조체 멤버를 사용할 때 순서를 적절히 조절해주면 크기를 많이 줄일 수 있습니다. 하지만  가독성은 떨이질 수 있으므로 상황에 맞게 코딩하면 됩니다. 

 

 
3.다양한 형태의 구조체 멤버

구조체의 멤버로는 다양한 자료형들을 쓸 수 있습니다. 멤버로 배열 선언도 가능하며 포인터도 넣을 수 있습니다. 구조체의 변수를 새로운 구조체의 멤버로도 사용가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct student
{
    char name[20];
    int age;
    double height;
    char *intro;
};
 
int main(void)
{
    struct student a;
    strcpy(a.name, "hong gil dong");
    a.age = 17;
    a.height = 187.5;
    a.intro = (char *)malloc(sizeof(char* 80);
    scanf("%s", a.intro);
    printf("이름 : %s\n", a.name);
    printf("나이 : %d\n", a.age);
    printf("키 : %.1lf\n", a.height);
    printf("소개 : %s\n", a.intro);
    free(a.intro);
    return 0;
}
cs

 

구조체 형을 다른 구조체의 멤버로 사용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#pragma warning(disable:4996)
#include <stdio.h>
#include <string.h>
struct profile
{
    char name[20];
    int age;
    double height;
};
struct student
{
    struct profile pf;
    int num;
    double grade;
};
 
int main(void)
{
    struct student a;
    strcpy(a.pf.name, "atom");
    a.pf.age = 17;
    a.pf.height = 187.5;
    a.num = 315;
    a.grade = 3.5;
    printf("이름 : %s\n", a.pf.name);
    printf("나이 : %d\n", a.pf.age);
    printf("키 : %.1lf\n", a.pf.height);
    printf("학번 : %d\n", a.num);
    printf("학점 : %.1lf\n", a.grade);
    return 0;
}
cs

 

 
4.구조체 변수의 초기화

구조체는 다음과 같이 초기화 할 수 있습니다.

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma warning(disable:4996)
#include <stdio.h>
#include <string.h>
struct student
{
    int num;
    char name[20];
    double grade;
};
 
int main(void)
{
    struct student a = { 315"atom"4.4 };
    struct student b = a;
    printf("이름 : %s\n", b.name);
    printf("학번 : %d\n", b.num);
    printf("학점 : %.1lf\n", b.grade);
    return 0;
}
cs

14::

구조채 변수의 대입 연산.
 b = a; 구조체 변수들끼리는 대입연산이 가능합니다. 구조체 변수를 함수의 인수로 주는 것도 가능하고 어떤 함수에서 반환값으로 구조체 변수의 값을 복사해서 반환하는 것도 가능하다. 구조체 변수도 하나의 변수처럼 처리됩니다. 
 
5.구조체 변수를 함수의 인수로 주고 반환 받기

포인터를 안쓰면 두 개 값을 바꾸는 함수는 불가능할까? 구조체로 묶어서 하나의 변수처럼 하나의 인수로 주면 됩니다. 함수가 오직 하나의 값을 반환한다는 조건을 만족시키면서 목적을 수행할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#pragma warning(disable:4996)
#include <stdio.h>
struct vision
{
    double left;
    double right;
};
 
struct vision swap(struct vision b)
{
    double temp;
    temp = b.left;
    b.left = b.right;
    b.right = temp;
    return b;
}
 
int main(void)
{
    struct vision a = { 1.5,2.0 };
    a = swap(a);
    printf("%.1lf, %.1lf\n", a.left, a.right);
}
cs
 
*reference : 이것이 C언어다.
독하게 시작하는 C프로그래밍

 

 

 

 

 

반응형