반응형

 

이번 시간에는 Flexible Array Member (FAM, 가변 배열 멤버)가 구조체 멤버 변수일 때, 그리고

공용체가 구조체의 멤버 변수일 때, 그 구조체의 전체 크기를 분석해본다. 

 

먼저, 분석할 구조체는 아래와 같다.

(내가 참고한 소스는 아래와 같다.)

https://github.com/strongswan/strongswan/blob/master/src/stroke/stroke_msg.h#L179

truct stroke_msg_t {
        /* length of this message with all strings */
        uint16_t length;

        /* type of the message */
        enum {
                ZERO,
                ONE,
        } type;

        int output_verbosity;

        union {
                struct {
                        char *name;
                } initiate;

                struct {
                        char *start;
                        char *end;
                } terminate_srcip;
        };
        /* length of the string buffer */
        uint16_t buflen;
        /* string buffer */
        char buffer[];
};

 

 

■ 구조체 분석

 

먼저, 멤버별 크기(64비트 기준) 및 오프셋을 이론적으로 분석해보자.  결론만 말하면, 이 구조체의 총 크기는 40 bytes 이다.

struct stroke_msg_t {
    unsigned short length;      // [offset:  0] 2 bytes
                                // [offset:  2] 패딩 2 bytes (enum int 정렬 맞추기)
    enum {
        STR_INITIATE,
        STR_ROUTE,
    } type;                     // [offset:  4] 4 bytes (enum → int 크기)

    int output_verbosity;       // [offset:  8] 4 bytes
                                // [offset: 12] 패딩 4 bytes (포인터 8byte 정렬 맞추기)

    union {                     // [offset: 16] 16 bytes (가장 큰 멤버 기준)
        struct {
            char *name;         // [offset: 16]  8 bytes (포인터)
        } initiate;             //               합계: 8 bytes

        struct {
            char *start;        // [offset: 16]  8 bytes (포인터)
            char *end;          // [offset: 24]  8 bytes (포인터)
        } terminate_srcip;      //               합계: 16 bytes
    };                          // union 크기 = max(8, 16) = 16 bytes

    unsigned short buflen;      // [offset: 32] 2 bytes
                                // [offset: 34] 패딩 6 bytes (구조체 정렬 8bytes 맞추기)

    char buffer[];              // [offset: 40] 0 bytes (Flexible Array Member)
};
// sizeof(struct stroke_msg_t) = 40 bytes

 

여기서 buffer 변수는 Flexible Array Member로써 크기를 차지하지 않는다. 이 변수는 sizeof에 포함되지 않으며, malloc으로 동적 할당 시 뒤에 가변 버퍼를 붙이는 패턴으로 사용된다.

 그리고 이 패턴의 변수를 활용하여 가변 배열(Flexible Array)을 추가하는 예제는 아래와 같다.

size_t extra = 128;
struct stroke_msg_t *msg = malloc(sizeof(*msg) + extra);
msg->buflen = extra;
// msg->buffer[0..127] 사용 가능

 

 

 

 

구조체에 패딩이 생긴 이유를 자세히 나타내면 아래와 같다.

unsigned short length;   // [offset: 0] 2 bytes → 다음 멤버가 enum(int, 4byte 정렬)
                         // [offset: 2] 패딩 2 bytes → offset 4 맞추기 위해

enum { ... } type;       // [offset: 4] 4 bytes → 다음 멤버도 int(4byte 정렬)
int output_verbosity;    // [offset: 8] 4 bytes → 다음 멤버가 포인터(8byte 정렬)
                         // [offset:12] 패딩 4 bytes → offset 16 맞추기 위해

union { ... };           // [offset:16] 16 bytes → 다음 멤버가 short(2byte 정렬)
unsigned short buflen;   // [offset:32] 2 bytes
                         // [offset:34] 패딩 6 bytes → 구조체 전체 크기를
                         //             가장 큰 멤버(포인터, 8byte)의 배수로 맞추기 위해

 

구조체 자체의 크기는 내부에서 가장 정렬 단위가 큰 멤버(여기서는 포인터 = 8 bytes)의 배수가 되어야 한다.

 

 

참고로 먼저, 우분투(Ubuntu) 시스템이 64비트(x86_64)인지 32비트(i386/i686)인지 확인하는 방법은 getconf 명령어로 확인한다.

$ getconf LONG_BIT
64

 

그리고 실제로 저 구조체 크기를 출력한 결과 40 bytes가 나오는 걸 확인했다.

int main (void) {
        printf("size: %d\n", sizeof(struct stroke_msg_t));
        return 0;
}

 

출력결과

size: 40
반응형