치다:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
구조의 크기는 각각 12와 8입니다.
이 구조물은 패딩 또는 포장되어 있습니까?
패딩 또는 포장은 언제 이루어 집니까?
padding
물건을 더 크게 만듭니다. packing
물건을 더 작게 만듭니다. 완전히 다릅니다.
치다:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
구조의 크기는 각각 12와 8입니다.
이 구조물은 패딩 또는 포장되어 있습니까?
패딩 또는 포장은 언제 이루어 집니까?
padding
물건을 더 크게 만듭니다. packing
물건을 더 작게 만듭니다. 완전히 다릅니다.
답변:
패딩은 구조 멤버를 "자연적인"주소 경계에 맞 춥니 다. 즉, int
멤버는 오프셋이 있으며 이는 mod(4) == 0
32 비트 플랫폼에 있습니다. 패딩은 기본적으로 켜져 있습니다. 첫 번째 구조에 다음과 같은 간격을 삽입합니다.
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;
};
6
32 비트 아키텍처 에서 크기의 구조를 생성 합니다.
그러나 x86 및 amd64와 같이이를 허용하는 아키텍처에서는 정렬되지 않은 메모리 액세스가 느리고 SPARC와 같은 엄격한 정렬 아키텍처 에서는 명시 적으로 금지됩니다 .
( 위의 대답은 아주 명확하게 이유를 설명하지만, 완전히 패딩의 크기가 해결되지 것 같다, 그래서, 내가 배운 내용에 따라 답변을 추가합니다 구조 포장의 잃어버린 예술 , 그것은에없는 한계까지 진화했다 C
, 그러나 또한 적용에 Go
, Rust
. )
규칙 :
int
주소를 4로 나누고 long
8 short
을 2 로 나눠서 시작해야합니다 .char
그리고 char[]
그들이 전에 패딩을 필요가 없습니다, 어떤 메모리 주소 수, 특별하다.struct
각각의 개별 부재에 대한 정렬 필요 이외의 경우 , 전체 구조체 자체의 크기는 끝에서 패딩함으로써 가장 큰 개별 부재의 크기로 나눌 수있는 크기로 정렬 될 것이다. long
를 8로 나눈 int
다음 4, short
2 로 나눌 수 있습니다.회원 순서 :
stu_c
및 stu_d
예제에서 동일한 멤버를 가지지 만 순서가 다르므로 두 구조체의 크기가 다릅니다.규칙 :
(n * 16)
바이트 에서 시작 합니다. ( 아래 예제에서 볼 수있는 구조체의 모든 16 진수 주소는으로 끝납니다 0
. ) long double
)입니다.char
as 멤버만 포함하면해당 주소는 모든 주소에서 시작할 수 있습니다.빈 공간 :
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입니다.
<The Lost Art of C Structure Packing>
은 규칙을 잘 설명하고 심지어이 답변보다 조금 더 길다고 생각했습니다. 이 책은 자유롭게 온라인으로 볼 수 있습니다 : catb.org/esr/structure-packing
나는이 질문이 오래되었다는 것을 알고 있으며 여기의 대부분의 대답은 패딩을 잘 설명하지만 직접 이해하려고 시도하는 동안 도움이되는 일의 "시각적"이미지를 갖는 것으로 나타났습니다.
프로세서는 명확한 크기 (워드)의 "청크"로 메모리를 읽습니다. 프로세서 워드의 길이가 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
키워드를 제공하여 구조 패딩을 억제합니다. 이에 대한 자세한 내용은 컴파일러 설명서를 참조하십시오.
따라서 패킹 된 구조는 패딩이없는 구조입니다.
일반적으로 패킹 된 구조가 사용됩니다
공간을 절약하기 위해
일부 프로토콜을 사용하여 네트워크를 통해 전송하기 위해 데이터 구조를 형식화하는 것은 물론
엔디안 을 처리 해야하기 때문에 좋은 방법은 아닙니다.
그것에 대한 엉덩이가 없습니다! 주제를 파악하려는 사람은 다음을 수행해야합니다.
- 정독 구조의 손실은 포장 아트 에릭 S. 레이몬드 쓴
- Eric의 코드 예제를 한눈에
- 마지막 으로 구조체가 가장 큰 유형의 정렬 요구 사항에 맞게 정렬되는 패딩에 대한 다음 규칙을 잊지 마십시오 .
패딩 규칙 :
규칙 2 : 왜 다음 구조를 고려합니까?
이 구조체의 배열 (2 구조체)을 만들려면 끝에 패딩이 필요하지 않습니다.
따라서 구조체의 크기 = 8 바이트
아래와 같이 다른 구조체를 만들어야한다고 가정 해 봅시다.
이 구조체의 배열을 만들려면 끝에 필요한 패딩 바이트 수의 두 가지 가능성이 있습니다.
A. 끝에 3 바이트를 추가하고 Long이 아닌 int에 맞게 정렬하면 :
B. 끝에 7 바이트를 추가하고 Long에 맞추면 :
두 번째 배열의 시작 주소는 8의 배수 (즉 24)입니다. 구조체의 크기 = 24 바이트
따라서 구조체의 다음 배열의 시작 주소를 가장 큰 멤버의 배수에 맞추면 (즉,이 구조체의 배열을 만들려면 두 번째 배열의 첫 번째 주소는 배수 인 주소에서 시작해야합니다) 여기에서 24 (3 * 8)), 끝에 필요한 패딩 바이트 수를 계산할 수 있습니다.
데이터 구조 정렬은 컴퓨터 메모리에서 데이터가 배열되고 액세스되는 방식입니다. 데이터 정렬 및 데이터 구조 패딩 이라는 두 가지 별도의 관련 문제로 구성됩니다 . 최신 컴퓨터가 메모리 주소를 읽거나 쓸 때 워드 크기의 청크 (예 : 32 비트 시스템의 4 바이트 청크) 이상에서이 작업을 수행합니다. 데이터 정렬은 데이터를 워드 크기의 배수와 같은 메모리 주소에 배치하는 것을 의미하며, 이는 CPU가 메모리를 처리하는 방식으로 인해 시스템 성능을 향상시킵니다. 데이터를 정렬하려면 마지막 데이터 구조의 끝과 다음 데이터의 시작 (데이터 구조 패딩) 사이에 의미없는 바이트를 삽입해야합니다.