구조체
- 여러 데이터 타입을 하나의 단위로 묶어 사용하는 기능
바이트 패딩(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;
}
'IVS > C 프로그래밍' 카테고리의 다른 글
[C] 공용체(union), 열거체(enum) (0) | 2025.01.13 |
---|---|
[C] 지역변수(local), 전역변수(global), 정적변수(static) (0) | 2025.01.13 |
[C] 배열 복사- memcpy, 비교- memcmp (0) | 2025.01.13 |
[C] 메모리 관련 (0) | 2025.01.13 |
[C] const 키워드 (0) | 2025.01.13 |