C의 구조 메모리 레이아웃


86

C # 배경이 있습니다. 저는 C와 같은 저수준 언어의 초보자입니다.

C #에서 struct의 메모리는 기본적으로 컴파일러에 의해 배치됩니다. 컴파일러는 데이터 필드를 재정렬하거나 암시 적으로 필드 사이에 추가 비트를 채울 수 있습니다. 따라서 정확한 레이아웃을 위해이 동작을 재정의하기 위해 몇 가지 특수 속성을 지정해야했습니다.

AFAIK, C는 struct기본적 으로 a의 메모리 레이아웃을 재정렬하거나 정렬하지 않습니다 . 그러나 찾기가 매우 어려운 약간의 예외가 있다고 들었습니다.

C의 메모리 레이아웃 동작은 무엇입니까? 무엇을 재정렬 / 정렬해야합니까?

답변:


111

C에서 컴파일러는 모든 기본 유형에 대해 일부 정렬을 지시 할 수 있습니다. 일반적으로 정렬은 유형의 크기입니다. 그러나 그것은 완전히 구현에 따라 다릅니다.

패딩 바이트가 도입되어 모든 객체가 올바르게 정렬됩니다. 재주문은 허용되지 않습니다.

아마도 모든 원격 최신 컴파일러 #pragma pack는 패딩을 제어하고 ABI를 준수하도록 프로그래머에게 맡기도록 구현합니다. (하지만 엄격하게 비표준입니다.)

C99 §6.7.2.1에서 :

12 구조체 또는 공용체 객체의 비트 필드가 아닌 각 멤버는 해당 유형에 적합한 구현 정의 방식으로 정렬됩니다.

13 구조 객체 내에서 비트 필드가 아닌 멤버와 비트 필드가 상주하는 단위에는 선언 된 순서대로 증가하는 주소가 있습니다. 적절하게 변환 된 구조 객체에 대한 포인터는 초기 멤버 (또는 해당 멤버가 비트 필드 인 경우 해당 멤버가 상주하는 단위)를 가리키고 그 반대의 경우도 마찬가지입니다. 구조 객체 내에 이름없는 패딩이있을 수 있지만 시작 부분에는 없습니다.


1
일부 컴파일러 (예 : GCC)는 동일한 효과를 구현 #pragma pack하지만 의미론에 대해보다 세밀한 제어를합니다.
Chris Lutz

21
나는 반대표를보고 놀랐습니다. 누구든지 오류를 지적 할 수 있습니까?
Potatoswatter

2
C11에는 _Alignas.
idmean

117

구현에 따라 다르지만 실제로 규칙 #pragma pack은 다음과 같습니다.

  • 구조체 멤버는 선언 된 순서대로 저장됩니다. (앞에서 언급했듯이 C99 표준에서 필요합니다.)
  • 필요한 경우 올바른 정렬을 보장하기 위해 각 구조체 멤버 앞에 패딩이 추가됩니다.
  • 각 기본 유형 T에는 sizeof(T)바이트 정렬이 필요합니다 .

따라서 다음 구조체가 주어집니다.

  • ch1 오프셋 0에 있습니다.
  • 패딩 바이트가 삽입되어 정렬됩니다.
  • s 오프셋 2에서
  • ch2 s 바로 뒤에 오프셋 4에 있습니다.
  • 정렬을 위해 3 개의 패딩 바이트가 삽입됩니다.
  • ll 오프셋 8에서
  • i ll 바로 뒤에 오프셋 16에 있습니다.
  • 4 개의 패딩 바이트가 끝에 추가되어 전체 구조체가 8 바이트의 배수가됩니다. 나는 이것을 64 비트 시스템에서 확인했다 : 32 비트 시스템은 구조체가 4 바이트 정렬을 갖도록 허용 할 수있다.

sizeof(ST)24도 마찬가지 입니다.

패딩을 피하기 위해 멤버를 재 배열하여 16 바이트로 줄일 수 있습니다.


3
필요한 경우 패딩이 앞에 추가됩니다. char귀하의 모범에 최종 구성원을 추가하는 것이 가장 좋습니다 .
Deduplicator

9
기본 유형은 반드시 sizeof(T)바이트 정렬이 필요하지 않습니다 . 예를 들어 double일반적인 32 비트 아키텍처의 경우 8 바이트이지만 4 바이트 정렬 만 필요한 경우가 많습니다 . 또한 구조체 끝에있는 패딩은 가장 넓은 구조체 멤버의 정렬에만 적용됩니다. 예를 들어, 3 개의 char 변수로 구성된 구조체에는 패딩이 없을 수 있습니다.
Matt

1
@ dan04, sizeof (T)의 내림차순으로 구조체를 레이아웃하는 것이 좋습니다. 이 작업에 단점이 있습니까?
RohitMat

11

데이터 정렬에 대한 더 나은 이해를 위해 데이터 구조 정렬 위키피디아 문서 를 읽는 것으로 시작할 수 있습니다 .

로부터 위키 피 디아 기사 :

데이터 정렬은 데이터를 워드 크기의 배수에 해당하는 메모리 오프셋에 배치하는 것을 의미하며, 이는 CPU가 메모리를 처리하는 방식으로 인해 시스템 성능을 향상시킵니다. 데이터를 정렬하려면 마지막 데이터 구조의 끝과 다음 데이터 구조 패딩의 시작 사이에 의미없는 바이트를 삽입해야 할 수 있습니다.

에서 6.54.8 구조-포장하는 pragma를 GCC의 문서의 :

Microsoft Windows 컴파일러와의 호환성을 위해 GCC는 이후에 정의되는 구조체 멤버 (폭이 0 인 비트 필드 제외), 공용체 및 클래스의 최대 정렬을 변경하는 일련의 #pragma 지시문을 지원합니다. 아래의 n 값은 항상 2의 작은 제곱이어야하며 새 정렬을 바이트 단위로 지정합니다.

  1. #pragma pack(n) 단순히 새로운 정렬을 설정합니다.
  2. #pragma pack() 컴파일이 시작될 때 적용된 정렬로 설정합니다 (명령 줄 옵션 -fpack-struct [=] 코드 생성 옵션 참조).
  3. #pragma pack(push[,n]) 내부 스택에 현재 정렬 설정을 푸시 한 다음 선택적으로 새 정렬을 설정합니다.
  4. #pragma pack(pop)정렬 설정을 내부 스택의 맨 위에 저장된 항목으로 복원하고 해당 스택 항목을 제거합니다. #pragma pack([n])이 내부 스택에는 영향 을 주지 않습니다. 따라서 #pragma pack(push) 여러 #pragma pack(n) 인스턴스 가 뒤 따르고 단일 #pragma pack(pop).

i386 및 powerpc와 같은 일부 대상은 문서화 #pragma된 .NET Framework를 구성 하는 ms_struct 를 지원합니다 __attribute__ ((ms_struct)).

  1. #pragma ms_struct on 선언 된 구조의 레이아웃을 켭니다.
  2. #pragma ms_struct off 선언 된 구조의 레이아웃을 끕니다.
  3. #pragma ms_struct reset 기본 레이아웃으로 돌아갑니다.

관심을 가져 주셔서 감사합니다. 안내대로 질문을 수정했습니다.
언일
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.