수학과의 좌충우돌 프로그래밍

C언어 - 사용자 정의 자료형 본문

프로그래밍 언어/C

C언어 - 사용자 정의 자료형

ssung.k 2018. 12. 30. 04:49


안녕하세요 강민성입니다.


오늘은 C언어 사용자 정의 자료형에 대해 알아보도록 하겠습니다.


자료형이란 무엇일까요?


자료형의 예시를 말하라고 한다면 쉽게 말할 수 있어도


자료형이 무엇인지에 대해서 물어보면 대답하기가 쉽지 않을 겁니다.


자료형의 정의는


'자료를 얼마만큼의 메모리 공간에 저장하고 읽을 것인가를 결정하는 것' 을 의미합니다.


우리가 흔히 쓰는 char, int, double 을 보면


각 1,4,8 byte 를 메모리 공간에 저장하고 읽게 됩니다.


이런 걸 자료형이라고 하는데, 사용자가 상황에 맞는 자료형을 지정해줄 수 도 있는 곳이죠.


지금 부터 함께 알아보도록 하겠습니다.



사용자 정의 자료형이란?




1. 구조체 



어렵지 않은 부분이니 예시를 통해 알아보겠습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
 
struct student
{
    int id;
    int age;
};
 
int main(void)
{
    struct student s1;
    s1.id = 2015540001;
    s1.age = 23;
    
    printf("학번은 %d 이고 나이는 %d세 입니다.\n",s1.id,s1.age);
   // 학번은 2015540001 이고 나이는 23세 입니다.
}
cs



student라는 사용자 정의 구조체를 사용한 후,


이를 다루는 모습입니다.


하나씩 살펴보도록 하겠습니다.


1
2
3
4
5
struct student
{
    int id;
    int age;
};
cs


구조체 형을 선언하는 방법입니다.


struct라는 예약어를 사용하여


struct 구조체의이름, 그리고 bracket( {} 의 이름입니다.) 안에


원래 정의되어있는 자료형을 사용하여 변수들을 선언합니다.


이를 구조체의 맴버 라고 합니다.


마지막으로 ; 로 잊으면 안됩니다.


1
2
3
struct student s1;
s1.id = 2015540001;
s1.age = 23;
cs


1 번째 줄은 위에서 선언한 구조체 자료형의 변수, s1을 선언했습니다.


보시는 바와 같이 struct 예약어와 구조체의 이름을 함께 하나의 자료형 이름으로 사용을 합니다.


다음으로는 구조체안의 맴버에 접근하는 방법인데 . 을 통해서 접근할 수 있습니다.


이 때 s1은 구조체 변수이지만 id와 age는 int 형 변수이기 때문에


int형 변수로서의 기능을 모두 수행합니다. 



그러면 이 구조체 변수는 메모리에 어떻게 할당이 될까요?


이를 알기 위해서는 바이트 얼라이먼트(byte alignment) 라는 개념을 알아야 합니다.



바이트 얼라이먼트



컴파일러는 구조체 맴버의 크기가 다른 경우에


 맴버 사이에 패딩 바이트를 넣어 맴버를 정렬하게 됩니다.


크기가 가장 큰 맴버가 할당 기준이 됩니다.


1
2
3
struct student s1;
s1.id = 2015540001;
s1.grade = 3.9;
cs


다음과 같은 경우에는 grade 맴버의 크기가 가장 크므로 8바이트를 기준으로 할당을 하고,


id 맴버는 남는 부분을 패딩 바이트로 채워주게 되는거죠.


      

 id

패딩바이트 

 grade


메모리에서 총 16바이트를 할당하게 되는 겁니다.


맴버를 어떤 순서로 선언하느냐에 따라 구조체의 크기가 달라지므로 구조체를 짤 때 고려해야 할 사항 중 하나입니다.





2. 구조체 활용


구조체의 맴버로는 다양한 형태가 들어갈 수 있습니다.


위에서 봤던 int , double 형 외에도 여러가지가 맴버로 들어갈 수 있습니다.


배열, 포인터, 이미 선언된 다른 구조체도 맴버로 사용이 가능합니다.


그리고 놀라운 자기 자신을 가르치는 포인터도 맴버가 될 수 있습니다.


그 예시를 살펴보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
 
struct list
{
    int num;
    struct list *next;
};
 
int main(void)
{
    struct list a = {10,0},b={20,0},c={30,0};
    struct list *head = &a, *current;
    
    a.next = &b;
    b.next = &c;
    current = head;
    while (current != NULL)
    {
        printf("%d\n",current->num); // 10 20 30
        current = current -> next;
    }
}
cs


위에서 말했던 자기 자신을 가르치는 포인터를 맴버로 가지는 걸 자기 참조 구조체  라고 합니다.


그리고 다음 예시를 이를 이용하여 연결리스트(linked list)를 만들어보았습니다.


함께 살펴보도록 하겠습니다.


1
2
3
4
5
struct list
{
    int num;
    struct list *next;
};
cs



구조체를 선언한 부분 입니다.


맴버로는 int형 num과 자기 자신을 가르키는 포인터 next가 존재합니다.


이 next 변수를 이용하여 다른 변수를 가르킬 수 있습니다.


아래서 다시 보도록 하죠.



1
2
3
4
5
6
struct list a = {10,0},b={20,0},c={30,0};
struct list *head = &a, *current;
    
a.next = &b;
b.next = &c;
current = head;
cs


struct list 구조체의 변수 a,b,c를 선언해주고 초기화 해주었습니다.


맴버의 순서대로 bracket 안에 써주면 초기화가 됩니다.


a,b,c의 num에는 각각 10,20,30이 들어가고 next에는 모두 NULL pointer가 들어갔습니다.


다음으로는 a.next에는 b의 주소를, b.next에는 c의 주소를 대입함으로서 각각의 변수들을 연결 시켜주었습니다.


이제 첫번째 a의 주소만 안다면 나머지 변수들은 포인터를 따라서 모두 사용이 가능해졌습니다.


그리고 head와 current에 첫번째 변수의 위치 a의 주소를 저장해주었습니다.


이렇게 head와 current에 따로 한 이유는,


head는 항상 첫번째 변수의 위치를 저장하고 있고 current를 움직임으로서 원하는 변수를 찾기 위해서 입니다.


1
2
3
4
5
6
7
while (current != NULL)
{
    printf("%d\n",current->num); // 10 20 30
    //printf("%d\n",(*current).num);
    current = current -> next;
    //current = (*current).next;
}
cs


이제 각 변수에 접근하여 값을 확인해보기 위한 작업입니다.


그전에 낯선 화살표가 보이는데요,


화살표는 아래 주석과 같은 역할을 하게 됩니다. 


* 를 통해 current의 값에 접근을 하게 되면 a가 되고


 . 를 통해 맴버 num에 접근을 하면 원하는 값인 10을 출력할 수 있는 것이죠.


이 때 주의 할 점은 . 이 * 보다 우선순위가 높으므로 꼭 괄호를 써줘야 합니다.


current =  current.next을 통해 다음 주소를 가르키며 주소가 NULL 이 되었을 때 반복문이 종료되게 됩니다.


 연결리스트(linked list) 는 활용도가 굉장히 높으니 알아두시면 많은 도움이 될 것입니다.





3. 공용체


공용체는 모든 맴버가 하나의 저장 공간을 같이 사용합니다. 


그 외에는 구조체와 모두 동일합니다.


예시를 보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
 
union student
{
    int age;
    double grade;
};
 
int main(void)
{
    union student s1 = {23};
    printf("나이 : %d\n",s1.age);  // 나이 : 23
    s1.grade = 3.9;
    printf("학번 : %.1lf\n",s1.grade); // 학번 : 3.9
    printf("나이 : %d\n",s1.age);  //나이 : 858993459
}
cs

부분씩 살펴보겠습니다.

1
2
3
4
5
union student
{
    int age;
    double grade;
};
cs

먼저 공용체를 선언하기 위해서는 구조체에서 쓴 struct대신 union을 사용합니다.

그 이외에는 모두 동일합니다.

1
union student s1 = {23};
cs



공용체는 저장공간이 하나이므로 초기화 하는 방법이 다릅니다.

다음과 같이 하나의 멤버만 초기화를 해주면 됩니다.

만약 첫번째 맴버가 아닌 다른 맴버에 대해 초기화를 하려고 한다면 

1
union student s1 = {.grade = 3.9};
cs


다음과 같이 . 연산자로 맴버를 직접 지정하면 됩니다.



1
2
3
4
5
union student s1 = {23};
printf("나이 : %d\n",s1.age);      // 나이 : 23
s1.grade = 3.9;
printf("학번 : %.1lf\n",s1.grade); // 학번 : 3.9
printf("나이 : %d\n",s1.age);      //나이 : 858993459
cs


다음으로 출력 결과를 보면 처음에는 나이가 잘 출력 되지만

학번을 대입한 이후에는 쓰레기 값이 나오는 걸 알 수 있습니다.

그 이유는 위에서도 말했지만 age와 geade가 같은 저장공간을 사용하기 때문입니다.

저장공간의 크기는 가장 큰 맴버에 의해 결정이 되므로 grade를 따라 8바이트가 됩니다.

따라서 grade 때문에 3.9를 저장한 값을 4바이트로 출력을 하면

 다음과 같이 의미없는 쓰레기 값이 출력이 되는 것입니다.



4. 열거형


마지막으로 열거형 입니다.

열거형은 변수에 저장할 수 있는 정수값을 기호로 정의하여 나열 한 것 입니다.

말로만 들어서는 무엇인지 이해가 안될 거 같은데 어려운 내용이 아니니

예시를 통해 알아보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
enum season {spring, summer, fall , winter};
 
int main(void)
{
    enum season ss;
    ss = spring;
    
    switch(ss)
    {
        case spring:
            printf("봄"); break;
        case summer:
            printf("여름"); break;
        case fall:
            printf("가을"); break;
        case winter:
            printf("겨울"); break;
    }
}
cs


3행에서는 열거형 선언이 이루어집니다.


예약어 enum 과 열거형 이름을 짓고, bracket안에 맴버들을 콤마로 나열 해줍니다.


이 때 열거형 변수는 저 맴버들의 값만 가질 수 있습니다.


컴파일 시, 컴파일러는 맴버들을 0부터 차례로 하나씩 큰 정수로 바꿔주게 됩니다.


1
enum season {spring=3, summer, fall=7 , winter};
cs


이 때 다음과 같은 방식으로 초기값을 바꿔 줄 수도 있습니다.


이럴 경우 summer은 4, winter은 8이 됩니다.


열거형 변수는 결국 정수가 들어간 공간, 즉 4바이트가 할당이 되게 됩니다.


이런 열거형 변수를 사용하는 이유는


프로그램을 짤 때 계절 이름을 직접 사용함으로서 코드의 가독성을 올릴 수 있기 때문입니다.



마무리


오늘은 여러가지 사용자 정의 자료형에 대해서 알아보았습니다.


이것저것 알아야 할 것은 많지만 크게 어려웠던 부분은 없었습니다.


프로그램의 질을 높이기 위해서 적절한 자료형을 사용하는게 좋은 프로그래머가 되는 길이라고 생각합니다.


저는 다음에 다시 뵙겠습니다.


지금까지 저의 글을 읽어주셔서 감사합니다 :)

'프로그래밍 언어 > C' 카테고리의 다른 글

C언어 - 메모리 동적할당  (0) 2018.12.28
Comments