IT이야기

C- 구조체의 메모리 정렬

cyworld 2021. 4. 12. 23:15
반응형

C- 구조체의 메모리 정렬


저는 32 비트 컴퓨터에서 작업하고 있으므로 메모리 정렬이 4 바이트 여야한다고 가정합니다. 구조체가 있다고 가정하십시오.

typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
} myStruct;

실제 크기는 6 바이트이고 정렬 된 크기는 8이어야한다고 가정하지만 sizeof(myStruct)6을 반환합니다.

그러나 내가 쓰면 :

typedef struct {
    unsigned short v1;
    unsigned short v2;
    unsigned short v3;
    int i;
} myStruct;

실제 크기는 10 바이트이고 정렬은 12이며 이번에는 sizeof(myStruct) == 12.

누군가 차이점이 무엇인지 설명 할 수 있습니까?


적어도 대부분의 컴퓨터에서 유형은 유형 자체만큼 큰 경계에만 정렬됩니다. [편집 : 배열을 만들 수 있어야하므로 그보다 더 많은 정렬을 요구할 수 없습니다. 패딩을 배열에 삽입 할 수 없습니다]. 구현에서 short분명히 2 바이트 및 int4 바이트입니다.

즉, 첫 번째 구조체가 2 바이트 경계에 정렬됩니다. 모든 멤버가 각각 2 바이트이므로 사이에 패딩이 삽입되지 않습니다.

두 번째 항목에는 4 바이트 경계에 맞춰 정렬되는 4 바이트 항목이 포함됩니다. 앞에 6 바이트가 있기 때문에 v3사이에 2 바이트의 패딩이 삽입되어 s i에 6 바이트의 데이터 short, 2 바이트의 패딩, 4 바이트의 데이터가 추가 int되어 총 12 개가됩니다.


멤버가 정확히 동일한 두 개의 구조체를 작성하더라도 다른 멤버를 갖는 것은 잊어 버리십시오 . 차이점 은 선언 된 순서가 다르면 각 구조체의 크기가 다를 수 있다는 것입니다 (종종 다를 수 있음).

예를 들어, 이것을보십시오,

#include <iostream>
using namespace std;
struct A
{
   char c;
   char d;
   int i; 
};
struct B
{
   char c;
   int i;   //note the order is different!
   char d;
};
int main() {
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
}

로 컴파일 gcc-4.3.4하면 다음 출력이 표시됩니다.

8
12

즉, 두 구조체의 멤버가 동일하더라도 크기가 다릅니다!

Ideone 코드 : http://ideone.com/HGGVl

결론은 표준이 패딩을 수행하는 방법에 대해 이야기하지 않기 때문에 컴파일러는 자유롭게 결정을 내릴 수 있으며 모든 컴파일러가 동일한 결정을 내릴 수 없다고 가정 수 없습니다 .


기본적으로 값은 크기에 따라 정렬됩니다. 따라서 a와 같은 2 바이트 값 short은 2 바이트 경계에 int정렬되고 an과 같은 4 바이트 값 은 4 바이트 경계에 정렬됩니다.

귀하의 예에서는 4 바이트 경계에 속 i하도록 2 바이트의 패딩이 전에 추가 i됩니다.

(전체 구조는 최소한 구조에서 가장 큰 값만큼 큰 경계에 정렬되므로 구조는 4 바이트 경계에 정렬됩니다.)

실제 규칙은 플랫폼에 따라 다릅니다. 데이터 구조 정렬 에 대한 Wikipedia 페이지에 자세한 내용이 있습니다.

일반적으로 컴파일러를 사용하면 #pragma pack지시문을 통해 패킹을 제어 할 수 있습니다 .


가정 :

sizeof(unsigned short) == 2
sizeof(int)            == 4

그런 다음 개인적으로 다음을 사용합니다 (컴파일러가 다를 수 있음).

unsigned shorts are aligned to 2 byte boundaries
int will be aligned to 4 byte boundaries.


typedef struct
{
   unsigned short v1;    // 0 bytes offset
   unsigned short v2;    // 2 bytes offset
   unsigned short v3;    // 4 bytes offset
} myStruct;              // End 6 bytes.


// No part is required to align tighter than 2 bytes. 
// So whole structure can be 2 byte aligned.

typedef struct
{
    unsigned short v1;      // 0 bytes offset
    unsigned short v2;      // 2 bytes offset
    unsigned short v3;      // 4 bytes offset
    /// Padding             // 6-7 padding (so i is 4 byte aligned
    int i;                  // 8 bytes offset
} myStruct;                 // End 12 bytes

// Whole structure needs to be 4 byte aligned.
// So that i is correctly aligned.

첫째, 패딩의 세부 사항은 컴파일러에 맡겨져 있지만 OS는 정렬 요구 사항에 대한 몇 가지 규칙을 적용합니다. 이 답변은 OS가 다를 수 있지만 gcc를 사용하고 있다고 가정합니다.

주어진 구조체와 그 요소가 차지하는 공간을 확인하려면 다음 규칙을 따를 수 있습니다.

먼저 구조체가 항상 모든 데이터 유형에 대해 적절하게 정렬 된 주소에서 시작한다고 가정합니다 .

그런 다음 구조체의 모든 항목에 대해 :

  • 필요한 최소 공간은에서 제공하는 요소의 원시 크기입니다 sizeof(element).
  • 요소의 정렬 요구 사항은 요소 기본 유형의 정렬 요구 사항입니다. 특히, 이것은 char[20]배열에 대한 정렬 요구 사항 이 일반 char.

마지막으로 구조체 전체의 정렬 요구 사항은 각 요소의 정렬 요구 사항의 최대 값입니다.

gcc는 주어진 요소 뒤에 패딩을 삽입하여 다음 요소 (또는 마지막 요소에 대해 이야기하는 경우 구조체)가 올바르게 정렬되도록합니다. 그것은 것입니다 결코 그 메모리를 절약 할 경우에도, 구조체의 요소의 순서를 다시 정렬합니다.

이제 정렬 요구 사항 자체도 약간 이상합니다.

  • 32 비트 Linux에서는 2 바이트 데이터 유형에 2 바이트 정렬이 필요합니다 (해당 주소는 짝수 여야 함). 모든 큰 데이터 유형은 4 바이트 정렬 (주소의 끝이 있어야합니다 0x0, 0x4, 0x8또는 0xC). 이는 4 바이트보다 큰 유형 (예 : doublelong double) 에도 적용됩니다 .
  • 32 비트 Windows는 유형이 K 바이트 인 경우 K 바이트로 정렬되어야한다는 점에서 더 엄격합니다. double이는는 0x0또는로 끝나는 주소에만 배치 될 수 있음을 의미합니다 0x8. 이에 대한 유일한 예외 long double는 실제로 길이가 12 바이트 임에도 불구하고 여전히 4 바이트로 정렬된다는 것입니다.
  • Linux 및 Windows의 경우 64 비트 시스템에서 K 바이트 유형은 K 바이트로 정렬되어야합니다. 다시 말하지만 long double은 예외이며 16 바이트로 정렬되어야합니다.

각 데이터 유형은 자체 크기의 메모리 경계에 정렬되어야합니다. 따라서 a short는 2 바이트 경계에 정렬 int되어야하고는 4 바이트 경계에 있어야합니다. 마찬가지로 long long는 8 바이트 경계에 있어야합니다.


첫 번째 구조체에서는 모든 항목이 크기 short이므로 전체 구조체를 short경계에 정렬 할 수 있으므로 끝에 패딩을 추가 할 필요가 없습니다.

두 번째 구조체에서 int (아마도 32 비트)는 워드로 정렬되어 v3사이에 패딩을 삽입 i하여 정렬해야합니다 i.


두 번째 이유 sizeof(myStruct)존재는 12사이에 삽입 된 패딩 도착하다 v3i정렬하도록 i32 비트 경계에서. 2 바이트가 있습니다.

Wikipedia explains the padding and alignment reasonably clearly.


The standard doesn't say much about the layout of structs with complete types - it's upto to the compiler. It decided that it needs the int to start on a boundary to access it, but since it has to do sub-boundary memory addressing for the shorts there is no need to pad them


Sounds like its being aligned to bounderies based on the size of each var, so that the address is a multiple of the size being accessed(so shorts are aligned to 2, ints aligned to 4 etc), if you moved one of the shorts after the int, sizeof(mystruct) should be 10. Of course this all depends on the compiler being used and what settings its using in turn.

ReferenceURL : https://stackoverflow.com/questions/5435841/memory-alignment-in-c-structs

반응형