구조 패딩 및 포장


209

치다:

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

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

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

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

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



24
C 구조 패킹의 잃어버린 기술 -catb.org/esr/structure-packing
Paolo

padding물건을 더 크게 만듭니다. packing물건을 더 작게 만듭니다. 완전히 다릅니다.
smwikipedia

답변:


264

패딩은 구조 멤버를 "자연적인"주소 경계에 맞 춥니 다. 즉, 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와 같은 엄격한 정렬 아키텍처 에서는 명시 적으로 금지됩니다 .


2
스파크에서 정렬되지 않은 메모리를 금지한다는 것은 일반적인 바이트 배열을 처리 할 수 ​​없다는 것을 의미합니까? 내가 아는 것처럼 구조 패킹은 바이트 배열을 구조체에 캐스트해야하고 배열이 구조체 필드에 맞는지 확인해야 할 때 데이터 전송 (예 : 네트워킹)에 주로 사용됩니다. 스파크가 그렇게 할 수 없다면 어떻게 작동합니까?!
Hi-Angel

14
IP, UDP 및 TCP 헤더 레이아웃을 보면 모든 정수 필드가 정렬되어있는 것을 알 수 있습니다.
Nikolai Fetissov 15:09에

17
은 "C 구조 포장의 분실 예술은"패딩 및 포장 ptimisations 설명 - catb.org/esr/structure-packing
Rob11311

3
첫 번째 회원이 먼저 와야합니까? 나는 전쟁이 구현에 전적으로 달려 있으며 (버전마다) 신뢰할 수 없다고 생각했다.
allyourcode

4
+ allyourcode 표준은 멤버 순서가 유지되고 첫 번째 멤버가 0 오프셋에서 시작되도록 보장합니다.
martinkunev

64

( 위의 대답은 아주 명확하게 이유를 설명하지만, 완전히 패딩의 크기가 해결되지 것 같다, 그래서, 내가 배운 내용에 따라 답변을 추가합니다 구조 포장의 잃어버린 예술 , 그것은에없는 한계까지 진화했다 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입니다.

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


4
"규칙"은 실제로 매우 명확 해졌으며, 어디에서나 직접적인 규칙을 찾을 수 없었습니다. 감사.
Pervez Alam

2
@PervezAlam이 책 <The Lost Art of C Structure Packing>은 규칙을 잘 설명하고 심지어이 답변보다 조금 더 길다고 생각했습니다. 이 책은 자유롭게 온라인으로 볼 수 있습니다 : catb.org/esr/structure-packing
Eric Wang

나는 그것을 시도 할 것입니다, btw는 구조 포장으로 제한됩니까? 책의 설명이 마음에 들었을 때 그냥 호기심을 느끼십시오.
Pervez Alam

1
@PervezAlam 매우 짧은 책입니다. 주로 c 프로그램의 메모리 풋 프린트를 줄이는 기술에 중점을 두며, 독서를 마치는 데 최대 며칠이 걸립니다.
Eric Wang

1
@ValidusOculus 예, 16 바이트 정렬됨을 의미합니다.
Eric Wang

44

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

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

변수 정렬

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

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

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

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

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

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


3
이것은 구조 패킹을 설명하지는 않지만 CPU 단어 정렬을 아주 잘 보여줍니다.
David Foerster


1
@ CiroSantilli709 大 抓捕 六四 事件 法轮功, 김프에 있었지만 하하를 통해 페인트 작업을하는데 시간을 절약했을 것 같습니다
IanC

1
오픈 소스 (Y) 이후로 더욱 우수
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

21

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

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

#pragma pack(n)

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

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

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

  • 공간을 절약하기 위해

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


5

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

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

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


1

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


1

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


1

패딩 규칙 :

  1. 구조체의 모든 멤버는 크기로 나눌 수있는 주소에 있어야합니다. 패딩은 요소 사이 또는 구조체의 끝에 삽입되어이 규칙이 충족되도록합니다. 이는 하드웨어에 의한보다 쉽고 효율적인 버스 액세스를 위해 수행됩니다.
  2. 구조체의 끝에 패딩은 구조체의 가장 큰 멤버의 크기에 따라 결정됩니다.

규칙 2 : 왜 다음 구조를 고려합니까?

구조 1

이 구조체의 배열 (2 구조체)을 만들려면 끝에 패딩이 필요하지 않습니다.

Struct1 배열

따라서 구조체의 크기 = 8 바이트

아래와 같이 다른 구조체를 만들어야한다고 가정 해 봅시다.

구조 2

이 구조체의 배열을 만들려면 끝에 필요한 패딩 바이트 수의 두 가지 가능성이 있습니다.

A. 끝에 3 바이트를 추가하고 Long이 아닌 int에 맞게 정렬하면 :

int에 정렬 된 Struct2 배열

B. 끝에 7 바이트를 추가하고 Long에 맞추면 :

Long에 정렬 된 Struct2 배열

두 번째 배열의 시작 주소는 8의 배수 (즉 24)입니다. 구조체의 크기 = 24 바이트

따라서 구조체의 다음 배열의 시작 주소를 가장 큰 멤버의 배수에 맞추면 (즉,이 구조체의 배열을 만들려면 두 번째 배열의 첫 번째 주소는 배수 인 주소에서 시작해야합니다) 여기에서 24 (3 * 8)), 끝에 필요한 패딩 바이트 수를 계산할 수 있습니다.


-1

데이터 구조 정렬은 컴퓨터 메모리에서 데이터가 배열되고 액세스되는 방식입니다. 데이터 정렬 및 데이터 구조 패딩 이라는 두 가지 별도의 관련 문제로 구성됩니다 . 최신 컴퓨터가 메모리 주소를 읽거나 쓸 때 워드 크기의 청크 (예 : 32 비트 시스템의 4 바이트 청크) 이상에서이 작업을 수행합니다. 데이터 정렬은 데이터를 워드 크기의 배수와 같은 메모리 주소에 배치하는 것을 의미하며, 이는 CPU가 메모리를 처리하는 방식으로 인해 시스템 성능을 향상시킵니다. 데이터를 정렬하려면 마지막 데이터 구조의 끝과 다음 데이터의 시작 (데이터 구조 패딩) 사이에 의미없는 바이트를 삽입해야합니다.

  1. 메모리에서 데이터를 정렬하기 위해, 메모리 할당 동안 다른 구조 멤버들에 대해 할당 된 메모리 어드레스들 사이에 하나 이상의 빈 바이트 (주소)가 삽입 (또는 비워짐)된다. 이 개념을 구조 패딩이라고합니다.
  2. 컴퓨터 프로세서의 아키텍처는 한 번에 메모리에서 1 워드 (32 비트 프로세서의 4 바이트)를 읽을 수있는 방식입니다.
  3. 프로세서의 이러한 이점을 이용하기 위해 데이터는 항상 4 바이트 패키지로 정렬되므로 다른 구성원의 주소 사이에 빈 주소가 삽입됩니다.
  4. C의 구조 패딩 개념으로 인해 구조의 크기는 항상 우리가 생각하는 것과 동일하지 않습니다.

1
답에서 같은 기사를 5 번 링크해야하는 이유는 무엇 입니까? 예제에 대한 링크는 하나만 유지하십시오. 또한 기사와 연결되므로 해당 사실을 공개해야합니다.
Artjom B.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.