[c] 구조 패딩 및 포장

치다:

struct mystruct_A
{
   char a;
   int b;
   char c;
} x;

struct mystruct_B
{
   int b;
   char a;
} y;

구조의 크기는 각각 12와 8입니다.

이 구조물은 패딩 또는 포장되어 있습니까?

패딩 또는 포장은 언제 이루어 집니까?



답변

패딩은 구조 멤버를 “자연적인”주소 경계에 맞 춥니 다. 즉, int멤버는 오프셋이 있으며 이는 mod(4) == 032 비트 플랫폼에 있습니다. 패딩은 기본적으로 켜져 있습니다. 첫 번째 구조에 다음과 같은 간격을 삽입합니다.

struct mystruct_A {
    char a;
    char gap_0[3]; /* inserted by compiler: for alignment of b */
    int b;
    char c;
    char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

반면에 Packing 은 컴파일러가 패딩을 수행하지 못하게합니다-GCC에서 명시 적으로 요청 __attribute__((__packed__))해야하므로 다음과 같습니다.

struct __attribute__((__packed__)) mystruct_A {
    char a;
    int b;
    char c;
};

632 비트 아키텍처 에서 크기의 구조를 생성 합니다.

그러나 x86 및 amd64와 같이이를 허용하는 아키텍처에서는 정렬되지 않은 메모리 액세스가 느리고 SPARC와 같은 엄격한 정렬 아키텍처 에서는 명시 적으로 금지됩니다 .


답변

( 위의 대답은 아주 명확하게 이유를 설명하지만, 완전히 패딩의 크기가 해결되지 것 같다, 그래서, 내가 배운 내용에 따라 답변을 추가합니다 구조 포장의 잃어버린 예술 , 그것은에없는 한계까지 진화했다 C, 그러나 또한 적용에 Go, Rust. )


메모리 정렬 (구조용)

규칙 :

  • 각 개별 구성원 앞에는 크기로 나눌 수있는 주소에서 시작하도록 패딩이 있습니다.
    예를 들어 64 비트 시스템에서는 int주소를 4로 나누고 long8 short을 2 로 나눠서 시작해야합니다 .
  • char그리고 char[]그들이 전에 패딩을 필요가 없습니다, 어떤 메모리 주소 수, 특별하다.
  • struct각각의 개별 부재에 대한 정렬 필요 이외의 경우 , 전체 구조체 자체의 크기는 끝에서 패딩함으로써 가장 큰 개별 부재의 크기로 나눌 수있는 크기로 정렬 될 것이다.
    예를 들어 구조체의 가장 큰 멤버 long를 8로 나눈 int다음 4, short2 로 나눌 수 있습니다.

회원 순서 :

  • 멤버의 순서는 구조체의 실제 크기에 영향을 줄 수 있으므로 명심하십시오. 예 를 들어 아래 stu_cstu_d예제에서 동일한 멤버를 가지지 만 순서가 다르므로 두 구조체의 크기가 다릅니다.

메모리의 주소 (struct 용)

규칙 :

  • 64 비트 시스템 구조
    주소는 (n * 16)바이트 에서 시작 합니다. ( 아래 예제에서 볼 수있는 구조체의 모든 16 진수 주소는으로 끝납니다 0. )
    이유 : 가능한 가장 큰 개별 구조체 멤버는 16 바이트 ( long double)입니다.
  • (업데이트) 구조체에charas 멤버만 포함하면해당 주소는 모든 주소에서 시작할 수 있습니다.

빈 공간 :

  • 두 구조체 사이의 빈 공간은 비 구조 변수에 의해 사용될 수 있습니다.
    예를 들어 test_struct_address()아래에서 변수 x는 인접한 구조체 g와 사이에 있습니다 h. 선언
    여부 x에 관계없이 h의 주소는 변경되지 않으며 x빈 공간을 재사용했습니다 g.
    의 경우도 마찬가지입니다 y.

( 64 비트 시스템 용 )

memory_align.c :

/**
 * Memory align & padding - for struct.
 * compile: gcc memory_align.c
 * execute: ./a.out
 */
#include <stdio.h>

// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
    int i;
    char c;
};

// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
    long l;
    char c;
};

// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
    int i;
    long l;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
    long l;
    int i;
    char c;
};

// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
    double d;
    int i;
    char c;
};

// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
    int i;
    double d;
    char c;
};

// size is 4,
struct stu_g {
    int i;
};

// size is 8,
struct stu_h {
    long l;
};

// test - padding within a single struct,
int test_struct_padding() {
    printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
    printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
    printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
    printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
    printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));

    return 0;
}

// test - address of struct,
int test_struct_address() {
    printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
    printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
    printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));

    struct stu_g g;
    struct stu_h h;
    struct stu_f f1;
    struct stu_f f2;
    int x = 1;
    long y = 1;

    printf("address of %s: %p\n", "g", &g);
    printf("address of %s: %p\n", "h", &h);
    printf("address of %s: %p\n", "f1", &f1);
    printf("address of %s: %p\n", "f2", &f2);
    printf("address of %s: %p\n", "x", &x);
    printf("address of %s: %p\n", "y", &y);

    // g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));

    // h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));

    // f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
    printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));

    // x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
    printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
    printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));

    // y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
    printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
    printf("space between %s and %s: %ld\n", "h", "y", (long)(&y) - (long)(&h));

    return 0;
}

int main(int argc, char * argv[]) {
    test_struct_padding();
    // test_struct_address();

    return 0;
}

실행 결과- test_struct_padding():

stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8

실행 결과- test_struct_address():

stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0  // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0  // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc  // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8  // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between h and y: 8

따라서 각 변수의 주소 시작은 g : d0 x : dc h : e0 y : e8입니다.

여기에 이미지 설명을 입력하십시오


답변

나는이 질문이 오래되었다는 것을 알고 있으며 여기의 대부분의 대답은 패딩을 잘 설명하지만 직접 이해하려고 시도하는 동안 도움이되는 일의 “시각적”이미지를 갖는 것으로 나타났습니다.

프로세서는 명확한 크기 (워드)의 “청크”로 메모리를 읽습니다. 프로세서 워드의 길이가 8 바이트라고 가정하십시오. 메모리를 8 바이트 빌딩 블록의 큰 행으로 간주합니다. 메모리에서 정보를 가져와야 할 때마다 해당 블록 중 하나에 도달하여 가져옵니다.

변수 정렬

위의 이미지에서 볼 수 있듯이 Char (1 바이트 길이)는 해당 블록 중 하나 안에 있기 때문에 CPU가 1 워드 만 처리해야하기 때문에 중요하지 않습니다.

4 바이트 int 또는 8 바이트 더블과 같이 1 바이트보다 큰 데이터를 처리 할 때 메모리에서 정렬되는 방식은 CPU에서 처리해야하는 워드 수에 차이를 만듭니다. 4 바이트 청크가 블록 내부에 항상 맞는 방식으로 정렬되는 경우 (메모리 주소는 4의 배수 임) 한 단어 만 처리하면됩니다. 그렇지 않으면 4 바이트의 청크가 한 블록에 자체 블록의 일부와 다른 블록에 일부를 가질 수 있으므로 프로세서가이 데이터를 읽기 위해 2 워드를 처리해야합니다.

8 바이트 더블에도 동일하게 적용되며, 이제는 항상 블록 안에있게하기 위해 8의 배수 메모리 주소에 있어야합니다.

이것은 8 바이트 워드 프로세서를 고려하지만이 개념은 다른 크기의 워드에도 적용됩니다.

패딩은 해당 데이터 사이의 간격을 채워서 해당 블록과 정렬되도록하여 메모리를 읽는 동안 성능을 향상시킵니다.

그러나 다른 답변에서 언급했듯이 때로는 공간 자체가 성능 자체보다 중요합니다. RAM이 많지 않은 컴퓨터에서 많은 양의 데이터를 처리하고있을 수 있습니다 (스왑 공간을 사용할 수는 있지만 훨씬 느림). 최소한의 패딩이 완료 될 때까지 (다른 답변에서 크게 예시되었으므로) 프로그램에서 변수를 정렬 할 수 있지만 충분하지 않으면 패딩을 명시 적으로 비활성화 할 수 있습니다. 이것은 패킹 입니다.


답변

구조 패킹은 구조 패딩, 정렬이 가장 중요한 패딩, 공간이 가장 중요한 패딩을 억제합니다.

일부 컴파일러는 #pragma패딩을 억제하거나 n 바이트 수로 압축하도록 제공합니다. 일부는이를 위해 키워드를 제공합니다. 일반적으로 구조 패딩을 수정하는 데 사용되는 pragma는 다음 형식입니다 (컴파일러에 따라 다름).

#pragma pack(n)

예를 들어 ARM은 __packed키워드를 제공하여 구조 패딩을 억제합니다. 이에 대한 자세한 내용은 컴파일러 설명서를 참조하십시오.

따라서 패킹 된 구조는 패딩이없는 구조입니다.

일반적으로 패킹 된 구조가 사용됩니다

  • 공간을 절약하기 위해

  • 일부 프로토콜을 사용하여 네트워크를 통해 전송하기 위해 데이터 구조를 형식화하는 것은 물론
    엔디안 을 처리 해야하기 때문에 좋은 방법은 아닙니다.


답변

패딩과 패킹은 같은 것의 두 가지 측면 일뿐입니다.

  • 패킹 또는 정렬은 각 부재가 반올림되는 크기입니다.
  • 패딩은 정렬과 일치하도록 추가 된 공간입니다.

에서는 mystruct_A, 4의 기본 배향을 가정하면, 각각의 멤버는 4 바이트의 배수에서 정렬된다. 크기가 있기 때문에 char1, 패딩 ac4 – 1 = 3 바이트의 패딩이 요구되지 않는 동안 int b되는 이미 4 바이트이다. 동일한 방식으로 작동합니다 mystruct_B.


답변

구조체 패킹은 컴파일러에게 구조체를 패킹하도록 명시 적으로 지시 한 경우에만 수행됩니다. 패딩은 당신이보고있는 것입니다. 32 비트 시스템은 각 필드를 단어 정렬에 채 웁니다. 컴파일러에게 구조를 패킹하도록 지시 한 경우 각각 6 바이트와 5 바이트입니다. 그래도하지 마십시오. 이식성이 없으며 컴파일러가 훨씬 느리고 때로는 버그가있는 코드를 생성합니다.


답변

그것에 대한 엉덩이가 없습니다! 주제를 파악하려는 사람은 다음을 수행해야합니다.