본문 바로가기
개발개발/c++

c++의 구조체, 구조체 크기와 구조체 멤버맞춤

by 유잉유잉유잉 2024. 12. 7.
728x90

 

 

🎯 구조체 

배열이 하나의 타입을 같은 이름으로 여러개 만들어서 사용한다면,

구조체는 여러 타입의 변수들을 하나로 모아서 사용할 수 있게 해주는 기능입니다.

 

struct 구조체이름
{
	변수선언
}
  • 구조체 이름 : 관련 있는 이름으로 작성하거나 사용하는 목적에 맞는 이름을 사용합니다.
  • 변수 : 다양한 타입의 변수들을 선언할 수 있습니다.
  • 언리얼에서 사용시 구조체 이름 앞에 대문자F를 붙여줍니다 ( ex: Fitem)

 

 

▶️ 구조체 선언과 사용

// 구조체 
struct FItem
{
    char name[32] = {};
    int option = 0;
    int price = 0;
}

int main()
{
    // 구조체 사용
    FItem item;
    // strcpy_s : 문자열 복사 함수. 오른쪽의 문자열을 왼쪽의 문자 배열에 복사해주는기능.
    strcpy_s( item.name, "목검" );
    item.option = 1;
    item.price = 1000;
 
    return 0;
}
  • 구조체변수. 을 이용하여 구조체의 멤버에 접근이 가능합니다. 
  • 구조체는 기본 접근한정자가 public 입니다.

 

 

 

▶️ 구조체의 활용 

  • 구조체도 배열을 이용해 사용할 수 있습니다.
  • 구조체 초기화도 가능합니다.
// 구조체타입 변수하나 만들어짐
// 구조체도 초기화 가능 = {};
// 전부 0으로 초기화
Fitem item = {};

// 선언과 동시에 값 넣기 가능
// 넣지 않은 값은 0으로 초기화
Fitem item2 = { "철검", EitemType::Weapon };

// 구조체도 배열 선언 가능.
Fitem itemArr[10];

 

 

 

 


▶️ 구조체 크기 확인하기 ⭐    

 구조체 사이즈는 정확히 계산할 줄 알아야합니다! 면접 단골 질문!

 

 

✔️ 예제1

enum class EItemType : unsigned char
{
	weapon,
	armor,
}

struct Fitem
{
	// 문자열 사용시 문자열 배열의 끝은 0 (null문자)로 저장되어야 한다.
	char		Name[32];	// 32바이트
	EItemType	ItemType;	// 1
	// bool test => 추가해도 동일하게 41바이트.
	int			Option;		// 4
	// bool test2 => 추가시 48바이트
	int			Price;		// 4
	// 41바이트가 나와야 하는데, 총 44바이트가 나오고 있음
};

int main()
{
	FItem item = {};
    
    std::cout << sizeof(FItem) << std::endl; // 44 출력
	
	return 0;
}
  • name : 32바이트
  • ItemType : 1바이트
  • option : 4바이트
  • price : 4바이트

=> 32 + 1 + 4 + 4 = 41인데 44가 출력됩니다 ( x64, 구조체 멤버맞춤 기본값 기준 )

  • 파일러가 구조체의 최적화를 위해 구조체 멤버맞춤을 합니다.
  • 4바이트 단위로 공간을 만들어서 변수들을 넣어줍니다 
    • 4바이트로 만드는 이유 : 기본 구조체 멤버맞춤 기본값일 경우, 제일 큰 변수의 크기 (FItem에서는int)가 4byte에 맞춰서 만들어지기 때문입니다. 
      • 바이트 정렬(Byte Aligment) : 자료형의 크기와 메모리상의 위치를 정렬하는것을 뜻합니다. 위 예시에선 제일 큰 변수인 int형 4byte의 크기에 맞춰서 바이트 정렬이 이뤄지는것을 확인하실 수 있습니다.
      • 바이트 패딩 (Byte Padding) : 바이트 정렬을 위해 추가될 수 있는 빈(남는) 공간입니다. 위 예시에선 EItemType의 크기는 1바이트이지만 남는 3바이트의 패딩이 생겼음을 알 수 있습니다.
      • 바이트 정렬과 바이트 패딩은 자료형의 크기와 메모리상의 위치를 정렬하기 위해 사용됩니다. 이는 cpu의 메모리 접근 시간과 효율성을 개선하기 위한 것입니다.

   | 4byte*8 = 32byte  : name + 남는공간 0 |

+ | 4byte : ItemType 1byte + 남는공간 3byte | 

+ | 4byte : option + 남는공간 0 |

+ | 4byte : price + 남는공간 0 | 

 

 

✔️ 예제 2 - 바이트정렬 ( 멤버맞춤 )의 기준 : 가장 큰 멤버 변수의 크기

enum EItemType : unsigned char
{
    weapon,
    armor,
};

struct FItem
{
    EItemType itemType;
};

int main()
{
    // 구조체 사용
    FItem item;

    std::cout << sizeof( FItem ) << std::endl;
    
    return 0;
}

FItem에서 제일 큰 변수의 크기가 1byte이기 때문에 1이 출력됩니다.

 

 

struct FItem
{
    EItemType itemType;
    char name[32] = {};
    int option = 0;
    int price = 0;
    __int64 big = 0;
};
  • 가장 큰 변수타입인 __int64 타입에 맞춰 8byte 단위로 구조체 멤버맞춤이 되며 

구조체의 크기는 총 56바이트가 됩니다.

 

 

 

✔️  예제 3 - 빈 구조체

  • 빈 클래스 or 빈 구조체 : 크기 출력시 1바이트가 나옵니다. 
    • 1바이트인 이유 : c++에서는 크기가 0인 독립 구조의 객체가 생기는것을 금지합니다. 메모리에 변수 선언하기 위한 최소 변수 단위로 공간을 미리 비워둡니다. 메모리에 할당하기 위한 최소 변수 단위로 맞춰집니다.   
    • 클래스도 동일합니다. 대신 클래스는 빈 클래스를 상속받을 경우에 예외가 발생하는데, 공백 클래스를 상속받을 경우 공백클래스의 크기는 0이 잡힙니다. 단일상속에만 적용됩니다.
      • 아무런 데이터를 저장하고 있지 않은 클래스의 의무적으로 할당되는 메모리를 절약하기 위해  사용하는 방법을 EBO(Empty Base Optimization), "공백 기본 클래스 최적화" 라고 합니다.

  • EBO를 적용한 CChild 클래스는 빈 클래스를 상속받았지만 변수 num의 int크기인 4바이트의 크기만 할당되는것을 확인하실 수 있습니다.

 

 

 

 

 

 


✔️  예제 4 - 배열이어도 변수의 크기에 맞춰집니다.

struct fTest1
{
	char name[32];
	char text ;
};
  • 33바이트. char(1byte)에 맞춰서 멤버맞춤이 1바이트가 되기 때문입니다.

 

 

 

 

✔️  예제 5 - 구조체 크기 최적화

struct FItem
{
    EItemType itemType;
    int option = 0;
    bool isActive = true;
};

+ | 4byte : ItemType 1byte + 남는공간 3byte | 

+ | 4byte : option 4byte + 남는공간 0 |

+ | 4byte : isActive 1byte + 남는공간 3byte | 

위 구조에선 변수 선언 순서대로 메모리 할당이 일어나기 때문에

실질적으론 6byte만(패딩까지 8byte) 필요하지만, 총 12byte를 사용하고 있습니다. 

 

struct FItem
{
    EItemType itemType;
    bool isActive = true;
    int option = 0;
};

위처럼 변수 생성 위치를 변경하여 메모리를 효율적으로 사용할 수 있습니다. 

 

 

 

✔️  예제 6 - 구조체 정렬 바이트의 기준 

struct FA
{
    int a;
    int b;
};

struct FB
{
    FA structA;
    int b;
};

위 예시에서 FA는 맞춤 4바이트, 총 크기가 8바이트입니다

FB는 제일 큰 멤버인 FA의 크기가 8바이트임에도 불구하고  맞춤이 4바이트, 총 크기가 12바이트가 나오고 있습니다. 

정렬 바이트(멤버맞춤)의 크기는 무조건 제일 큰 멤버가 아님을 알 수 있습니다. 

 

 

✔️  예제 6-2 - 구조체 정렬 바이트의 기준 

struct FA
{
    int a;
    int b;
    __int64 big;
};

struct FB
{
    FA structA;
    int b;
};

예제 6에서 FA의 멤버변수로 __int64 타입의 big 변수가 추가되었습니다.

FB의 맞춤은 8바이트로 

정렬 바이트(멤버맞춤)의 기준은 가장 큰 멤버의 크기가 아닌 가장 큰 멤버 변수임을 알 수 있습니다. 

 

 

 

 

 

▶️ 출력하지 않고 구조체, 클래스 크기 확인하기

visual studio 에서 std::cout 을 출력하지 않고도 구조체와 클래스의 크기가 확인 가능합니다.

확인하고자 하는 구조체의 이름에 마우스 커서를 가져다대면

구조체의 크기를와 멤버맞춤 사이즈 확인하실 수 있습니다.

 

 

 

 

▶️  Visual Studio에서 구조체 멤버맞춤 옵션 확인하기

프로젝트 오른쪽마우스 -> 속성 

C/C++ - 코드생성 - 구조체 멤버맞춤

 

 

 

 

 

▶️ 구조체 멤버맞춤 관련 참고사항 

  • 나중에 서버 구조체 멤버맞춤이 1바이트고, 클라는 구조체 멤버맞춤이 4바이트일 경우, 맞지가 않아서 에러나는 경우가 있다고 합니다. 서버와 클라는 맞춰야 합니다.
  • 클래스도 동일한 기준 입니다.
728x90

댓글