데이터 신호를 주고 받을 때, 일반적으로 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비트만 차지하게 된다

+ Recent posts