본문 바로가기

IT/C|C++

[C] 구조체, Structure

이번 포스팅에서는 구조체, Structure에 대해서 포스팅하겠습니다.


1. 구조체, Structure Type


구조체와, Structure Type이라는 단어를 보면, "구조"라는 단어가 눈에 들어옵니다. 쉽게 이야기해서 구조체는 구조화된 데이터의 집합입니다.




2. 왜 사용하는가?


우리가 만약 직교 좌표계를 표현하기 위해서 프로그래밍을 한다고 생각해봅시다.



일단 직교좌표계에서 점을 표현하기 위해서는 원소가 2개가 필요하고, 다음과 같이 표현할 수 있습니다.



이를 C언어에서 표현을 한다라고 하면 다음과 같이 표현할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
 
int main(void){
 
    int x = 2;
    int y = 3;
 
    return 0;
}
 
 




이게 직교 좌표계의 한점을 C언어에서 표현한 방법입니다.


그럼 직교 좌표계에서 여러점을 동시에 표현한다고 해봅시다.


그럼 이렇게 표현될 수 있습니다.



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
#include <stdio.h>
 
int main(void){
 
    int x_1 = 0;
    int y_1= 0;
 
 
    int x_2 = 1;
    int y_2= 1;
 
 
    int x_3 = 2;
    int y_3= 2;
 
 
    int x_4 = 3;
    int y_4= 3;
 
 
    int x_5 = 4;
    int y_5= 4;
 
    return 0;
}
 
 




바뀐 코드를 보면 해당 점마다 변수 이름이 모두 다르기 때문에 접근할 때마다 변수 이름을 하나하나씩 써야한다는 불편한 점이 있습니다.


이는 for문 같은 반복문을 쓸 때, 너무 힘이 듭니다.


자 그럼 이걸 배열을 써서 다음과 같이 만들어봅시다.



1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
int main(){
    int x[5];
    int y[5];
 
    int i;
    for(i=0; i < 5; i++){
        x[i] = i
        y[i] = i
    }
 
}




이런 형태로 바꿀 수 있습니다.


그래도 뭔가 불편합니다. 그냥 이렇게 추상적으로 int x, int y로 표현하는 것보다 이건 좌표야! 라고 표현할 수 있는 무언가가 필요할 것 같습니다.


이럴 때, 쓰는 것이 구조체입니다.


3. 어떻게 쓰는가?


일단 먼저 예제를 보겠습니다.

해당 코드는 mac에서 테스트되었습니다.

1. main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
 
struct coordinate{
    char tag[100];
    double x;
    double y;
};
 
int main(void){
    struct coordinate coord;
 
    coord.x = 1.0;
    coord.y = 1.0;
    strcpy(coord.tag, "Quadrant 1");
 
    printf("coord.tag: %s,\tcoord.x: %f,\t coord.y: %f\n", coord.tag, coord.x, coord.y);
 
    return 0;
}
 



2. Makefile

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
 
VPATH=./
EXEC=main
OBJDIR=./
 
CC=gcc
 
CFLAGS=-Wall -Wfatal-errors
 
OBJ=main.o
 
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard ./*.h) Makefile
 
$(EXEC): $(OBJS)
    $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS)
 
$(OBJDIR)%.o: %.c $(DEPS)
    $(CC) $(COMMON) $(CFLAGS) -c $< -o $@
 
.PHONY: clean
 
clean:
    rm -rf $(OBJS) $(EXEC)
 




main.c 코드를 봅시다.



처음에 구조체를 선언법을 확인할 수 있습니다.



구조체를 선언하기 위한 예약어는 "struct"입니다. 해당 코드에서는 coordinate 라는 이름의 구조체를 선언했습니다.


그리고 그 구조체의 멤버로는 tag, x, y를 데이터 형태에 맞게 선언했습니다. 



main함수에서는 구조체 사용법을 확인할 수 있습니다.



main 함수에서 해당 구조체 타입을 사용하기 위해서 struct coordinate coord라고 선언해주었습니다.


이는 coordinate라고 명명된 구조체 타입인 변수 coord를 선언한다라는 의미와 같습니다.



구조체 내부 변수의 접근 방법은 "." 연산자를 이용해서 접근합니다. 


예제 코드에서 coord내부의 x라는 멤버 변수에 "."연산자로 접근하여 1.0이라는 숫자를 입력한 것을 확인 할 수 있습니다.  (coord.x = 1.0;)


이것이 구조체의 제일 기본적인 사용법입니다.


자, 이제 구조체 사용에 대한 응용을 알아봅시다.


4. 응용 #1. typedef


앞에서 구조체를 정의한 후, 사용할때는 struct coordinate coord 형태로 사용을 해줬습니다.

int 와 같이 데이터 타입을 선언하는 것처럼 struct coordinate 를 선언하는 것인데, 이를 int 와 같이 데이터 타입처럼 쓰고 싶습니다.

그럴땐, typedef을 사용하면 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <string.h>
 
typedef struct coordinate{
    char tag[100];
    double x;
    double y;
}COORDINATE;
 
int main(void){
    COORDINATE coord;
 
    coord.x = 1.0;
    coord.y = 1.0;
    strcpy(coord.tag, "Quadrant 1");
 
    printf("coord.tag: %s,\tcoord.x: %f,\t coord.y: %f\n", coord.tag, coord.x, coord.y);
 
    return 0;
}


  

처음 예제와 거의 같습니다. 다른 점은 typedef과 tag명 COORDINATE를 썼다는 정도이고, main 함수에서는 그 전에 쓰던 

struct coordinate 가 COORDINATE로 바뀌었다는 점입니다.


5. 응용 #2. 중첩 구조체


구조체는 구조체 안의 멤버 변수로 구조체를 포함할 수 있습니다.

먼저 예제를 보겠습니다.


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
#include <stdio.h>
#include <string.h>
 
typedef struct coord_information{
    char information[100];
    char quadrant;
}COORD_INFORMATION;
 
typedef struct coordinate{
    COORD_INFORMATION info;
    double x;
    double y;
}COORDINATE;
 
int main(void){
 
    COORDINATE coord;
 
    coord.x = 1.0;
    coord.y = 1.0;
    coord.info.quadrant='1';
    strcpy(coord.info.information, "Coordinate system");
 
    printf("coord.info.information: %s,\tcoord.info.quadrant: %c\n", coord.info.information, coord.info.quadrant);
    printf("coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
 
  




코드 상단에서 2가지 종류의 구조체를 선언한 것을 확인할 수 있습니다.

그리고 그 중 하단에 있는 COORDINATE 구조체는 상단에 있는 COORD_INFORMATION 구조체를 멤버변수로 포함하고 있는 것을

확인 할 수 있습니다.

그리고 중첩된 구조체의 접근은 "."연산자를 두번 써서 접근할 수 있습니다.

해당 예제 하단에 그 예시가 잘 나와있습니다.

6. 응용 #3. 함수 포인터


구조체 안에는 함수 포인터를 멤버 변수로 가질 수 있습니다.

이러한 기법을 통해서 OOP의 class의 method property를 흉내낼 수 있습니다.

예제 코드를 먼저 보겠습니다.


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
#include <stdio.h>
#include <string.h>
 
typedef struct coordinate COORDINATE;
 
struct coordinate{
    float x;
    float y;
    void (*setCoordinate)(COORDINATE* coord, floatfloat);
};
 
void set_coord(COORDINATE* coord, float x, float y){
    coord->= x;
    coord->= y;
}
 
int main(void){
 
    COORDINATE coord;
    coord.setCoordinate = set_coord;
    coord.setCoordinate(&coord,1.01.0);
 
 
    printf("coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
 
    return 0;
}
 





(1) coordinate라는 structure를 COORDINATE라는 타입으로 재정의를 해준다고 먼저 선언을 합니다.

그 이유는 바로 밑에 나오는 구조체 coordinate에 함수 포인터 파라미터에 데이터 타입 COORDINATE가 포함되어 있기 때문입니다.

만약에 typedef을 먼저 사용해주지 않는다면, 컴파일시, 컴파일러가 COORDINATE라는 type을 찾을 수 없다는 에러를 출력하게됩니다.



(2) 구조체 coordinate의 멤버 변수로 float x, float y, void (*setCoordinate)(COORDINATE* coord, float, float)을 선언해줍니다.

여기서 void (*setCoordinate)(COORDINATE* coord, float, float)는 함수 리턴이 void형태이며, 파라미터 COORDINATE* coord, float, float를

갖는 함수 포인터 setCoordinate을 멤버변수로 갖겠다는 표현입니다.


(3) 함수 set_coord에 대한 정의를 합니다.

구조체 COORDINATE의 포인터를 받아서, 해당 구조체 멤버 변수 x, y의 값을 파라미터로 받은 x,y의 값으로 입력해주는 함수입니다.



(4) COORDINATE 구조체 변수를 선언하고, 해당 구조체 변수의 포인터 멤버 변수 setCoordinate의 변수 값을 set_coord의 주소값으로 입력합니다.


(5) 해당 구조체의 멤버 함수처럼 사용을 합니다.




7. 응용 #4. 초기화


1. Makefile

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
 
VPATH=./
EXEC=main
OBJDIR=./
 
CC=gcc
 
CFLAGS=-Wall -Wfatal-errors
 
OBJ=main.o
 
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard ./*.h) Makefile
 
$(EXEC): $(OBJS)
    $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS)
 
$(OBJDIR)%.o: %.c $(DEPS)
    $(CC) $(COMMON) $(CFLAGS) -c $< -o $@
 
.PHONY: clean
 
clean:
    rm -rf $(OBJS) $(EXEC)
 



2. main.c

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
#include <stdio.h>
#include <string.h>
 
typedef struct coordinate{
    char tag[100];
    double x;
    double y;
}COORDINATE;
 
int main(void){
 
 
    COORDINATE coord = {"Quadrant 1",1.01.0};
    printf("coord.tag: %s,\tcoord.x: %f,\t coord.y: %f\n", coord.tag, coord.x, coord.y);
 
    COORDINATE coord_1 = {"Quadrant 1",1.01.0};
    memset(&coord_1, 0sizeof(COORDINATE));
    printf("coord.tag: %s,\tcoord.x: %f,\t coord.y: %f\n", coord_1.tag, coord_1.x, coord_1.y);
 
    COORDINATE coord_2 = {.x = 1.0, .y = 1.0, .tag="Hello"};
    printf("coord.tag: %s,\tcoord.x: %f,\t coord.y: %f\n", coord_2.tag, coord_2.x, coord_2.y);
 
    return 0;
}
 



3. 실행화면




구조체의 초기화는 여러가지 방식이 있는데, C89의 구조체 초기화, memset을 이용한 구조체 초기화, C99의 제한적인 초기화 방법이 있습니다.


구조체 coord의 초기화는 C89의 구조체 초기화이며, 구조체 선언시, 변수의 선언 순서대로 해당 값을 예제와 같이 입력해주면 됩니다.


구조체 coord1의 초기화는 memset함수를 이용한 초기화이며, memset(메모리 주소, 저장할 값, 해당 메모리 크기)순으로 입력하시면 됩니다.


예제에서 구조체 초기화시 tag = Quadrant 1, x = 1.0, y = 1.0으로 초기화해줬는데, memset을 이용하여 메모리를 0으로 바꾼것을 확인할 수 


있습니다.


구조체 coord2의 초기화는 C99의 초기화로 구조체의 특정 변수들만 골라서 초기화할 수 있는 방법입니다.



8. 응용 #5 복사


1. Makefile

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
 
VPATH=./
EXEC=main
OBJDIR=./
 
CC=gcc
 
CFLAGS=-Wall -Wfatal-errors
 
OBJ=main.o
 
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard ./*.h) Makefile
 
$(EXEC): $(OBJS)
    $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS)
 
$(OBJDIR)%.o: %.c $(DEPS)
    $(CC) $(COMMON) $(CFLAGS) -c $< -o $@
 
.PHONY: clean
 
clean:
    rm -rf $(OBJS) $(EXEC)
 



2. main.c


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>
 
typedef struct coordinate{
    double x;
    double y;
}COORDINATE;
 
int main(void){
 
    COORDINATE coord;
    COORDINATE coord1;
 
    coord.x = 1.0;
    coord.y = 1.0;
 
    memcpy(&coord1, &coord, sizeof(COORDINATE));
 
    printf("coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
    printf("coord1.x: %f,\t coord1.y: %f\n", coord1.x, coord1.y);
 
    return 0;
}
 



3. 실행화면






  • 좌표를 나타내는 coordinate라는 구조체에 double형 변수 x,y를 정의합니다.
  • typedef을 이용하여 struct coordinate를 COORDINATE라는 새로운 데이터형으로 만든다.
  • COORDINATE라는 변수 coord, coord1을 생성한다.
  • coord.x와 coord.y에 각각 1.0이라는 데이터를 입력한다.
  • memcpy함수를 이용하여 coord의 구조체 내용을 coord1에 복사한다.
  • 복사가 잘 되었는지 확인하기 위해서 해당 구조체 변수를 출력한다.



8. 응용 #5 함수 인자로 구조체 전달하기


1. Makefile


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
 
VPATH=./
EXEC=main
OBJDIR=./
 
CC=gcc
 
CFLAGS=-Wall -Wfatal-errors
 
OBJ=main.o
 
OBJS = $(addprefix $(OBJDIR), $(OBJ))
DEPS = $(wildcard ./*.h) Makefile
 
$(EXEC): $(OBJS)
    $(CC) $(COMMON) $(CFLAGS) $^ -o $@ $(LDFLAGS)
 
$(OBJDIR)%.o: %.c $(DEPS)
    $(CC) $(COMMON) $(CFLAGS) -c $< -o $@
 
.PHONY: clean
 
clean:
    rm -rf $(OBJS) $(EXEC)
 
 



2. main.c


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
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <string.h>
 
typedef struct coordinate{
    double x;
    double y;
}COORDINATE;
 
void func(COORDINATE coord);
void _func(COORDINATE* coord);
 
int main(void){
 
    COORDINATE coord;
 
    coord.x = 1.0;
    coord.y = 1.0;
 
    printf("MAIN -> coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
    func(coord);
    printf("MAIN -> coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
    _func(&coord);
    printf("MAIN -> coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
 
    return 0;
}
 
void func(COORDINATE coord){
    coord.x = 3.0;
    coord.y = 3.0;
    printf("FUCNTION -> coord.x: %f,\t coord.y: %f\n", coord.x, coord.y);
}
 
void _func(COORDINATE* coord){
    coord->= 5.0;
    coord->= 5.0;
    printf("FUCNTION -> coord.x: %f,\t coord.y: %f\n", coord->x, coord->y);
}
 



3. 실행화면





  • 좌표를 나타내는 coordinate라는 구조체에 double형 변수 x,y를 정의합니다.
  • typedef을 이용하여 struct coordinate를 COORDINATE라는 새로운 데이터형으로 만듭니다.
  • COORDINATE coord를 인자로 하는 함수 func와 COORDINATE* coord를 인자로 하는 함수 _func을 정의합니다.
  • func은 내부 변수의 값을 각각 3.0으로 변경하고 이를 출력합니다.
  • _func은 내부 변수의 값을 각각 5.0으로 변경하고 이를 출력합니다.
  • COORDINATE라는 변수 coord 생성합니다.
  • coord.x와 coord.y에 각각 1.0이라는 데이터를 입력합니다.
  • coord의 내부 변수의 내용을 출력합니다.
  • func의 파라미터로 coord를 줍니다.
  • 해당 구조체 변수를 출력합니다.
  • _func의 파라미터로 coord의 주소를 줍니다.(&coord)
  • 해당 구조체 변수를 출력한다.