51.1 구조체 크기 알아보기

구조체 정렬을 하기 전에 먼저 구조체의 크기부터 알아보겠습니다. 구조체의 전체 크기는 sizeof 연산자를 사용하면 알 수 있습니다.

  • sizeof(struct 구조체)
  • sizeof(구조체별칭)
  • sizeof(구조체변수)
  • sizeof 구조체변수

이제 가상의 네트워크 패킷 구조체 PacketHeader를 정의해서 멤버의 크기와 구조체의 크기를 구해봅니다. 다음 내용을 소스 코드 편집 창에 입력한 뒤 실행해보세요.

struct_sizeof.c

#include <stdio.h>

struct PacketHeader {
    char flags;    // 1바이트
    int seq;       // 4바이트
};

int main()
{
    struct PacketHeader header;

    printf("%d\n", sizeof(header.flags));           // 1: char는 1바이트
    printf("%d\n", sizeof(header.seq));             // 4: int는 4바이트
    printf("%d\n", sizeof(header));                 // 8: 구조체 전체 크기는 8바이트
    printf("%d\n", sizeof(struct PacketHeader));    // 8: 구조체 이름으로 크기 구하기

    return 0;
}

실행 결과

1
4
8
8

구조체의 크기를 구하려면 sizeof(header)처럼 sizeof 연산자 안에 변수를 넣어주거나 sizeof(struct PacketHeader)처럼 struct 키워드와 구조체 이름을 넣어주면 됩니다.

PacketHeader 구조체 안에는 1바이트 크기의 char와 4바이트 크기의 int가 들어있습니다. 그래서 전체 크기는 5바이트가 나와야 할 것 같은데 실제로는 8바이트가 나왔습니다.

C 언어에서는 구조체를 정렬할 때 멤버 중에서 가장 큰 자료형 크기배수로 정렬합니다. 여기서 가장 큰 자료형은 int이므로 int의 크기 4바이트를 기준으로 정렬합니다.

그림 51‑1 구조체 정렬

4바이트로 정렬해서 flagsseq가 모두 들어가는 최소 크기는 8바이트입니다(4바이트 * 2). 따라서 구조체 정렬을 하면 5바이트가 아닌 8바이트가 됩니다.

여기서는 1바이트 크기의 char flags 뒤에는 4바이트를 맞추기 위해 남는 공간에 3바이트가 더 들어갑니다. 이렇게 구조체를 정렬할 때 남는 공간을 채우는 것을 패딩(padding)이라고 부릅니다.

그럼 정말 구조체를 정렬한 뒤 멤버의 위치가 그림 51-1처럼 되었는지 확인해보겠습니다. 구조체에서 멤버의 위치(offset)를 구할 때는 offsetof 매크로를 사용합니다(stddef.h 헤더 파일에 정의되어 있습니다).

  • offsetof(struct 구조체, 멤버)
  • offsetof(구조체별칭, 멤버)

struct_offsetof.c

#include <stdio.h>
#include <stddef.h>   // offsetof 매크로가 정의된 헤더 파일

struct PacketHeader {
    char flags;    // 1바이트
    int seq;       // 4바이트
};

int main()
{
    printf("%d\n", offsetof(struct PacketHeader, flags));    // 0
    printf("%d\n", offsetof(struct PacketHeader, seq));      // 4

    return 0;
}

실행 결과

0
4

offsetof 매크로에 구조체와 멤버를 지정하면 구조체에서 해당 멤버의 상대 위치가 반환됩니다(첫 멤버의 상대 위치는 0). 여기서는 구조체가 4바이트 단위로 정렬하므로 seq의 위치는 1이 아닌 4가 나옵니다.