본문 바로가기
IVS/C 프로그래밍

[C] 구조체 - 바이트 패딩(Byte Padding)

by 코곰_ 2025. 1. 13.

구조체

- 여러 데이터 타입을 하나의 단위로 묶어 사용하는 기능

 

 

 

바이트 패딩(Byte Padding)?

  • 구조체 내 멤버들이 특정 크기(정렬 규칙)에 맞게 배치되는 과정
  • 메모리 정렬을 최적화하기 위한 방법. 

- 구조체의 멤버들이 메모리에 저장될 때, CPU가 효율적으로 접근할 수 있도록 추가되는 빈 공간

- 컴파일러는 구조체의 각 멤버를 메모리에 배치할 때, 특정 정렬 규칙을 따른다. 이 규칙은 CPU가 데이터를 읽고 쓸 때 최적의 성능을 발휘할 수 있도록 돕는다.

- 구조체의 크기 할당은 크기가 가장 큰 자료형을 기준으로 배수만큼 커진다. 

 

 

 

 

메모리 정렬

  • CPU의 메모리 접근 효율을 높이기 위해 각 데이터 타입이 자신의 크기에 맞는 메모리 주소에 배치되도록 하는 규칙을 따른다.
  • 예를 들어, int는 일반적으로 4바이트 경계에 배치되는 것이 성능상 유리하다. 이런 이유로 char와 같은 작은 데이터형이 뒤따를 때 메모리 공간이 패딩되어서 그 크기만큼 빈 공간이 삽입된다.

32bit CPU의 경우 한 번에 4byte 단위로 공간을 읽어온다. (64bit CPU의 경우 한 번에 8byte)

 

 

예시

struct Example {
    char a;  // 1 byte
    int b;   // 4 bytes
    char c;  // 1 byte
};

 

  • 위 구조체에서 char a는 1바이트를 차지하고, int b는 4바이트를 차지.
  • 그러나 char c가 추가되면, 메모리에서 a와 c 사이에 3바이트의 패딩이 추가되어 b가 4바이트 정렬을 유지할 수 있도록 함.
  • 따라서 이 구조체의 전체 크기는 12바이트

-> 가장 큰 데이터 크기에 따라 정렬

 

 

 

 

(예시 코드1)

#include <stdio.h>
#include <string.h>
typedef struct {
    char c_1;      // 1 바이트
    char c_2;      // 1 바이트
    char c_3;      // 1 바이트
    char c_4;      // 1 바이트
    int i_1;       // 4 바이트
} Dest;

int main() {
    char source[8] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x03, 0x02, 0x01}; 
    Dest destination;
    memcpy(&destination, &source,sizeof(destination));

    // memcpy는 가장 낮은 주소부터 넣는다. 
    printf("c_1 = %02x\n",destination.c_1); // 01
    printf("c_2 = %02x\n",destination.c_2); // 02
    printf("c_3 = %02x\n",destination.c_3); // 03
    printf("c_4 = %02x\n",destination.c_4); // 04
    printf("i_1 = %08x\n",destination.i_1); // 01020304
    return 0;
}

 

 

 

 

(예시 코드2)

#include <stdio.h>
#include <string.h>
typedef struct {
    char c_1;      // 1 바이트
    int i_1;       // 4 바이트
    int i_2;       // 4 바이트
} Dest;

int main() {
    char source[12] = {0x01, 0x04, 0x03, 0x02, 0x01, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11};
    Dest destination;

    memcpy(&destination, &source,sizeof(destination)); 
    printf("c_1 = %02x\n",destination.c_1); // 01
    printf("i_1 = %08x\n",destination.i_1); // 07060501
    printf("i_2 = %08x\n",destination.i_2); // 11100908
    return 0;
}

 

위의 코드에서, dest의 메모리 배치는 다음과 같다.

1. c_1에 source[0] 값인 0x01이 복사(1바이트)

2. int타입인 i_1의 4바이트 정렬을 맞추기 위해 패딩(3바이트)

3. i_1의 시작 부분인 4바이트 공간에는 source[1] ~ source[4] 값인 0x07, 0x06, 0x05, 0x01이 복사(4바이트)

4. i_2의 시작 부분에는 source[5] ~ source[8] 값인 0x11, 0x10, 0x09, 0x08이 복사(4바이트)

따라서, 전체 구조체 크기는 12바이트 = 1(char) + 3(padding) + 4(int) + 4(int)

 

 

 

(예시 코드3)

#include <stdio.h>
typedef struct {
    char c;      // 1 바이트
    int i;       // 4 바이트
    double d;    // 8 바이트
} Dest;

    int main() {
    Dest destination;
    printf("sizeof(destination) = %u\n", sizeof(destination)); // 16
    return 0;
}

전체 구조체 크기는 16바이트 = 1(char) + 3(padding) + 4(int) + 8(double)

 

 

 

(예시 코드4)

#include <stdio.h>
typedef struct {
    char c;      // 1 바이트
    double d;    // 8 바이트
    int i;       // 4 바이트
} Dest;

    int main() {
    Dest destination;
    printf("sizeof(destination) = %u\n", sizeof(destination)); // 24
    return 0;
}

전체 구조체 크기는 24바이트 = 1(char) + 3(padding) + 8(double) + 4(int) + 4(padding)

 

 

 

(예시 코드5)

#include <stdio.h> 
#include <stdint.h> 
#include <string.h>
typedef struct { 
    uint8_t field1 : 4;
    uint8_t field2 : 4; 
    uint8_t field3 : 4; 
    uint8_t field4 : 4; 
} BitFieldData;
int main() {
    uint8_t dataArray[2] = { 0x2A, 0x5B }; // Binary: 00101010 01011011
    BitFieldData data1; // 2 byte
    // 복사할 데이터를 배열에서 추출
    memcpy(&data1, dataArray, sizeof(BitFieldData));
    printf("Data 1:\n"); 
    printf("Field 1: %x\n", data1.field1); // A
    printf("Field 2: %x\n", data1.field2); // 2
    printf("Field 3: %x\n", data1.field3); // B
    printf("Field 4: %x\n", data1.field4); // 5
    return 0;
}

 

 

 

 

 

 

 

 

#pragma pack

  • C, C++에서 구조체(struct), 공용체(union), 클래스(class) 등의 메모리 정렬 단위(alignment)를 변경하는 데 사용되는 컴파일러 지시문
  • 구조체 멤버는 자신의 크기 또는 컴파일러의 기본 정렬 단위(보통 4바이트나 8바이트) 기준으로 배치된다.
  • 그러나 #pragma pack을 사용하면 정렬 단위를 직접 지정할 수 있다. 이를 통해 패딩을 줄이고 구조체 크기를 줄이는 것이 가능하다.

 

 

왜 사용할까?

만약 각각 컴퓨터 시스템이 다르고 각자에게 맞는 단위로 바이트 패딩이 이루어지면, 네트워크 통신 과정에서 값들이 이상한 값으로 인식될 수 있다. 따라서 네트워크 통신을 할 때 패킷의 사이즈를 정확히 지정해야 하고, 이러한 문제를 #pragma pack을 이용하여 해결할 수 있다. 

 

 

 

문법

#pragma pack(n)

 

 

예시 코드

#include <stdio.h>

#pragma pack(1) // 정렬 단위를 1바이트로 설정
typedef struct {
    char c;    // 1 바이트
    int i;     // 4 바이트
} PackedStruct;
#pragma pack() // 기본 정렬로 복원

typedef struct {
    char c;    // 1 바이트
    int i;     // 4 바이트
} DefaultStruct;

int main() {
    printf("PackedStruct size: %zu\n", sizeof(PackedStruct));  // 5
    printf("DefaultStruct size: %zu\n", sizeof(DefaultStruct)); // 8
    return 0;
}

 

출력

PackedStruct size: 5
DefaultStruct size: 8

 

 


~수정중~

(예시 코드6) - 고쳐야함

#include <stdio.h> 
#include <stdint.h>
typedef struct BitFieldData{
    unsigned int BR :3; // 3 1 2 1 1
    unsigned int Reserved: 1;
    unsigned int BR_RATIO: 2;
    unsigned int Reserved: 1;
    unsinged int SWK_EN: 1;
}BitFieldData;


int main() { 
    BitFieldData data;
    // 구조체의 각 비트별 맞는 값을 넣어보자 


    uint8_t target;
    memcpy(&target,&data,sizeof(target));
    for (int i = 7; i >= 0; i--) {
    printf("%d", (target & (1 << i)) ? 1 : 0);
    }
    return 0;
}