데이터 신호를 주고 받을 때, 일반적으로 1 byte 단위가 자주 사용된다.
주소는 byte 단위이다. 리틀 엔디안을 잊지 말자!
#include <stdint.h>
#include <string.h>
int main()
{
uint8_t target[5] = { 0xAB, 0x12, 0x34, 0x56, 0xCD };
union _Data_
{
uint8_t receiveData[5];
struct {
uint8_t head;
uint8_t body[3];
uint8_t tail;
}msg;
}data;
memcpy(&data, target, 5);
return 0;
}
C 구조체
struct
타입을 모아 새로운 타입을 만드는 문법
c와 c++에서 차이가 있다
- c에서는 만들고 난 뒤, 한꺼번에 초기화 불가능 (선언할 때는 가능)
- c에서는 선언할 때 앞에 struct 붙여줘야 함
기본 문법
struct AB
{
int a;
int b;
};
int main()
{
struct AB ab;
ab.a = 1;
ab.b = 2;
return 0;
}
구조체 초기화는 선언할 때만 가능
구조체 특정 멤버를 선언 시, 선택해서 초기화할 수 있음
구조체를 만들면서 변수를 만들 수 있음
구조체 내부에 구조체를 만들 수 있음 (구조체의 이름은 필요하지 않아서 생략 가능)
typedef
기존 타입을 원하는 이름으로 정의해서 사용할 수 있다
아래 예시에서는 struct AB 를 SAB로 typedef 하였다.
위 방식보다는 아래 방식이 더 많이 사용된다
아예 구조체 이름을 생략해버릴 수도 있다
union
생긴 건 구조체와 비슷하지만 멤버끼리 값을 공유한다!
아래 예시를 보자
union 안에 있는 두 변수의 시작 메모리 주소가 같은 것을 확인할 수 있다.
주소는 byte 단위로 리틀 엔디안이 적용되는 것을 확인할 수 있다.
위 내용을 적용해보자
#include <stdint.h>
#pragma pack(1) // 여기서 부터 패딩 사용 안함
typedef union {
uint8_t ori[6];
struct {
uint8_t opcode;
uint8_t lba_part1 : 5;
uint8_t reserved : 3;
uint8_t lba_part2;
uint8_t lba_part3;
uint8_t length;
uint8_t control;
} field;
}Node;
#pragma pack(4) // 여기서 부터 패딩 사용
#pragma는 뭐고 uint8_t lba_part1 : 5; 뒤에 붙은 ' : ' 는 뭘까 ?
padding에 대한 개념을 알아보자!
padding
아래 상황을 보자
AB 구조체에서 a 는 char라서 1 byte인데 뒤에 3byte가 비고 b가 오는 것을 확인할 수 있다.
이는 CPU가 값을 편하게 읽을 수 있도록 컴파일러가 padding을 줘서 그렇다.
CPU가 4byte 씩 읽을 수 있다면 아래와 같은 차이가 생긴다.
하지만 여기서 문제가 생기는데 컴파일러마다 padding을 주는 조건이 다르다
따라서 padding을 주지 않도록 설정해주는 매크로가 바로 #pragma이다.
uint8_t lba_part1 : 5 에서 :은 비트 필드(bit-field)의 크기를 지정하는 부분이다.
비트 필드는 구조체의 각 멤버가 1비트 또는 그 이상의 비트 단위로 할당될 수 있도록 합니다.
이렇게 설정할 경우 uint8_t 타입에서 5비트만 사용 하고,
1byte 내에서 5비트만 차지하게 된다
'C language' 카테고리의 다른 글
변수 키워드 static과 extern (1) | 2024.12.14 |
---|---|
전처리지시문, Header Guard (1) | 2024.12.14 |
고정길이 정수 - stdint.h (0) | 2024.12.13 |
포인터와 문자열 char v[4] = "ABC" ; vs const char* v = "ABC";는 어떻게 다를까? (0) | 2024.12.12 |
c언어로 shell script 실행하기 (0) | 2024.12.12 |