본문 바로가기

BackEnd/C

구조체의 추가적인 특성과 메모리 관계

반응형

1.  구조체 변수도 구조체의 멤버가 될 수 있다.


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
#include <stdio.h>
#define PI  3.14
 
typedef struct __point
{
    double xPos;
    double yPos;
} point;
 
typedef struct __circle
{
    point center;      // 원의 중심
    double rad;    // 반지름
} circle;
 
void ShowCircleInfo(const circle * ptr)
{
    printf("원의 중심: [%g, %g] \n"
        (ptr->center).xPos, (ptr->center).yPos);
    printf("원의 넓이: %g \n",
        (ptr->rad)*(ptr->rad)*PI);
}
 
int main(void)
{
    circle cl={
        {1.12.2},    // center 초기화
        2.5      // 반지름 초기화
    };
 
    ShowCircleInfo(&cl);
 
    return 0;
}

cs


1
2
원의 중심: [1.12.2]
원의 넓이: 19.625
cs


• 12행 : 구조체 point의 변수를 구조체 circle의 멤버로 정의하고 있다. 이처럼 구조체의 멤버로 다른 구조체의 변수가 정의될 수 있다. 


• 16행 : circle 구조체의 주소 값을 인자로 전달받을 수 있도록 함수가 정의되었다. 


• 26~29행 : 구조체 변수를 멤버로 두고 있는 구조체 변수의 초기화 방법을 보여준다. 제일 먼저 초기화할 대상이 구조체 변수이기 때문에 중괄호가 등장하였고, 이어서 초기화할 대상이 실수이기 때문에 실수 값이 등장하였다.



위 예제의 26행에서 생성되는 구조체 변수는 다음과 같은 형태로 할당 및 초기화된다.



그리고 이 구조체 변수의 주소값이 31행에서 ShowCirclelnfo 함수의 인자로 전달이 되므로, 16행


에 정의되어 있는 매개변수 ptr과의 관계는 다음과 같다.



따라서 원의 중심 좌표를 출력하기 위해서 19행과 같은 연산식이 만들어졌으며, 원의 넓이를 계산


기 위해서 21행과 같은 연산식이 만들어졌다.



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
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h>
#define PI  3.14
 
typedef struct __point
{
    double xPos;
    double yPos;
} point;
 
typedef struct __circle
{
    point * cntPtr;
    double rad;   
} circle;
 
void ShowCircleInfo(const circle * ptr)
{
    printf("원의 중심: [%g, %g] \n"
        (ptr->cntPtr)->xPos, (ptr->cntPtr)->yPos);
    printf("원의 넓이: %g \n",
        (ptr->rad)*(ptr->rad)*PI);
}
 
int main(void)
{
    circle cl={NULL2.5};
    cl.cntPtr=(point*)malloc(sizeof(point));
 
    cl.cntPtr->xPos=1.1;
    cl.cntPtr->yPos=2.2;
 
    ShowCircleInfo(&cl);
    free(cl.cntPtr);
    return 0;
}

cs


1
2
원의 중심: [1.12.2]
원의 넓이: 19.625
cs


• 13행 : point 구조체 변수를 가리킬 수 있도록 포인터가 멤버로 정의되었다. 


• 27행 : circle 구조체 변수를 초기화하고 있다. 첫 번째 멤버인 포인터 cntPtr은 NULL로 초기화하고,번째 멤버인 rad는 2.5로 초기화 하였다. 


• 28행 : 구조체 point의 크기만큼을 힙 영역에 할당하고, 반환되는 주소 값을 저장하고 있다. 이렇게 구조체 변수도 힙 영역에 얼마든지 동적으로 할당할 수 있으며, 할당의 방법은 기본 자료형 변수의 동적 할당방법과 동일하다. 


• 33행 : 함수가 호출되었을 때, 구조체 변수 cl과 매개변수 ptr의 메모리 관계를 그림으로 정리해 보겠다.


구조체 변수 cl은 main 함수 내에서 선언되었지만, cl의 멤버 cntPtr이 가리키는 변수는 malloc 함


에 의해서 할당되었으므로, 위 그림처럼 각각이 존재하는 메모리 영역이 구분된다.


3. 어떤 모델이 더좋을까?


하나의 구조체가 다른 하나의 구조체를 포함하는 방식에는 두 가지가 있음을 설명하였다. 


그 중 하나는 1번의 멤버 변수의 형태로 정의하는 방식이고, 다른 하나는 2번의 포인터를 이용해서 참조만 하는 방식이다. 



왼쪽은 1번에서 구현한 모델이다. 이 모델은 구조체 변수 center를 내부에 포함하는 모델이다.


오른쪽은 2번에서 구현한 모델이다. 이 모델은 외부에 선언된 구조체 변수 center를 참조하는 모


델이다. 구현방식과 모델의 형태는 다르지만, 물리적 관점이 아닌 논리적인 관점에서 포함의 형


태를 취한다는 데에는 차이가 없다. 두 모델 모두 합리적이고 유용하게 사용이 되는 모델이기 때


문에 둘 다 기억할 필요가 있다.



4. 구조체 변수의 주소 값은 첫 번째 멤버의 주소 값이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
 
typedef struct __point
{
    double xPos;
    double yPos;
} point;
 
 
int main(void)
{
    point pnt={1.12.2};
 
    printf("구조체 pnt의 주소: %#x \n"&pnt);
    printf("구조체 pnt의 첫 번째 멤버의 주소: %#x \n"&(pnt.xPos));
 
    return 0;
}

cs


1
2
구조체 pnt의 주소: 0x62fe40
구조체 pnt의 첫 번째 멤버의 주소: 0x62fe40
cs


5. 자기 참조 구조체


자기자신과 동일한 자료형의 구조체 변수를 참조할 수 있도록 정의된 구조체


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
#include <stdio.h>
 
typedef struct box
{
    int data;
    struct box * boxRef;
} box;
 
 
int main(void)
{
    int i;
    box * bxPtr;
 
    box b1={1NULL};
    box b2={11NULL};
 
    b1.boxRef=&b2;
    b2.boxRef=&b1;
 
    bxPtr=&b1;
 
    for(i=1; i<=10; i++)
    {
        printf("%3d", bxPtr->data);
        (bxPtr->data)++;
        bxPtr=bxPtr->boxRef;
        if(!(i%2))
            printf("\t");
    }
 
    return 0;
}
cs


1
  1 11    2 12    3 13    4 14    5 15
cs



3행에 정의되어 있는 구조체를 먼저 관찰해 보자.



box형 구조체 변수는 둘 이상 선언될 수 있다. 그래서 하나의 box형 구조체 변수가 다른 box형 구


체 변수의 주소 값을 저장할 수 있는 형태로 정의 되었다.


• 18행이 실행되면 구조체 변수의 관계는 다음과 같이 형성된다.



• 19행이 실행되면 구조체 변수의 관계는 다음과 같이 변경된다.



간단히 말해서 구조 체 변수 b1과 b2가 서로를 참조하는 형태로 변경이 된다. 


결론적으로 구조체 box의 멤버로는 구조체 box의 포인터 변수가 올 수 있다.


6.  구조체 변수는 정렬되어 할당된다.


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
#include <stdio.h>
 
typedef struct box1
{
    int AAA;
    short BBB;
    short CCC;
} box1;
 
typedef struct box2
{
    short BBB;
    int AAA;
    short CCC;
} box2;
 
int main(void)
{
    box1 bx1;
    box2 bx2;
 
    printf("구조체 box1의 변수 크기: %d \n"sizeof(bx1));
    printf("구조체 box2의 변수 크기: %d \n"sizeof(bx2));
 
    return 0;
}

cs


1
2
구조체 box1의 변수 크기: 8
구조체 box2의 변수 크기: 12
cs

위의 출력결과에서는 구조체 boxl의 크기를 8바이트,box2의 크기를 12바이트라고 말하고 있다. 


구조체 box1과 box2의 유일한 차이점은 구조체 멤버를 배치한 순서 인데,이러한 차이를 보이는 이


유는 메모리 공간에 구조체 변수를 할당할 때, 멤버의 접근 용이성을 위해서 특정 바이트 크기 단


위로 정렬을 하기 때문이다. 예를 들어서 구조체 변수를 4바이트 단위로 정렬해서 할당하는 컴파일


러가 있다고 가정해보자. 그렇다면 이 컴파일러는 메모리 공간을 다음과 같은 방식으로 나눈다.




이로써 구조체를 정의할 때, 구조체 멤버의 배치 순서를 어떻게 가져가는 것이 좋을지에 대한 기준


도 나름대로 세워졌을것이다.

반응형

'BackEnd > C' 카테고리의 다른 글

C 모아보기  (0) 2021.01.09
I/O 인풋, 아웃풋에 대한 이해  (0) 2019.10.07
구조체 배열  (0) 2019.03.23
구조체의 정의에 포함되는 typedef 선언  (0) 2019.03.23
구조체 변수로 가능한것과 불가능한 것  (0) 2019.03.22