구조 패딩 및 패킹
고려사항:
struct mystruct_A
{
char a;
int b;
char c;
} x;
struct mystruct_B
{
int b;
char a;
} y;
구조물의 크기는 각각 12와 8입니다.
이 구조물은 패딩되어 있습니까, 아니면 패킹되어 있습니까?
패딩 또는 패킹은 언제 이루어집니까?
패딩은 구조 구성원을 "자연스러운" 주소 경계에 맞춥니다. 예를 들어,int
는 오프셋을 .오프셋은 버 offsets members members members 、 ' members 、 ' 。이거는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;
한편, 패킹은 컴파일러가 패딩을 하는 것을 방지합니다.이것은 명시적으로 요구되어야 합니다.GCC에서는, 이 패딩은__attribute__((__packed__))
음음음같 뭇매하다
struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};
크기 구조를 만들어 낼 것이다.6
32살
주의: 비정렬 메모리 액세스는 x86이나 amd64 등의 아키텍처에서는 더 느리고 SPARC 등의 엄밀한 얼라인먼트 아키텍처에서는 명시적으로 금지되어 있습니다.
(위의 답변은 이유를 명확하게 설명했지만 패딩의 크기에 대해서는 완전히 명확하지 않은 것 같습니다.따라서 "구조 패킹의 잃어버린 기술"에서 배운 내용에 따라 답변을 덧붙입니다.이 답변은 에 한정되지 않고 에도 적용할 수 있도록 진화했습니다.)
메모리 얼라인먼트(구조용
규칙:
- 각 멤버 앞에 패딩이 있어 멤버의 크기에 따라 분할할 수 있는 주소로 시작합니다.
들어 64비트int
로 나누어진 주소에서 시작해야 합니다.long
8 ★★★★★★ ★short
2까지. char
★★★★★★★★★★★★★★★★★」char[]
특별한 메모리 주소일 수 있기 때문에 패딩이 필요 없습니다.- ★★★의
struct
각 개별 부재에 대한 정렬 필요성 이외에 전체 구조물의 크기는 가장 큰 개별 부재의 크기로 분할할 수 있는 크기로 끝에 패딩을 통해 정렬됩니다.
들어 큰가 '아주 큰 부재'인 경우long
8, 8로int
후는 4입니다.short
2번
회원순서:
- 을 줄 수 를 들어, 「구조의 크기」와 같이 해 .
stu_c
★★★★★★★★★★★★★★★★★」stu_d
아래 예에서 볼 수 있듯이 멤버는 같지만 순서가 다르므로 두 구조물에 대해 크기가 다릅니다.
메모리 내 주소(구조체용)
규칙:
- system
는 " "에서 시작합니다.(n * 16)
바이트(아래 예에서 볼 수 있듯이 모든 출력된 16진수 주소는 로 끝납니다).
이유: 개개의 구조 멤버 중 가장 큰 것은 16바이트입니다.long double
를 참조해 주세요. - (갱신) 구조체에 포함된 것이
char
멤버로서 주소는 임의의 주소로부터 개시할 수 있습니다.
빈 공간:
- 두 구조물 사이의 빈 공간은 적합할 수 있는 비구조 변수에 의해 사용될 수 있습니다.
::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;
}
실행 결과 - :
stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8
실행 결과 - :
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 입니다.
이 질문이 오래되고 여기 있는 대부분의 답변은 패딩을 잘 설명한다는 것을 알지만, 제가 직접 이해하려고 노력하면서 무슨 일이 일어나고 있는지에 대한 "시각적인" 이미지가 도움이 된다는 것을 알게 되었습니다.
프로세서는 일정한 크기(워드)의 메모리를 「청크」로 읽습니다.CPU 워드의 길이가 8바이트라고 합니다.메모리를 8바이트 빌딩 블록의 큰 행으로 간주합니다.메모리로부터 정보를 취득할 필요가 있을 때마다, 그 블록 중 하나에 도달해 취득합니다.
위의 그림과 같이 Char(1바이트 길이)가 어디에 있든 상관없습니다.Char(1바이트 길이)는 이러한 블록 중 하나 안에 있기 때문에 CPU는 1개의 워드만 처리해야 하기 때문입니다.
4바이트 int나 8바이트 더블과 같이 1바이트보다 큰 데이터를 처리할 경우, 메모리 내의 정렬 방법에 따라 CPU에 의해 처리되어야 하는 워드의 수가 달라집니다.4바이트 청크가 블록의 안쪽에 항상 맞도록 정렬되어 있으면 (메모리 주소가 4의 배수인 경우) 하나의 워드만 처리하면 됩니다.d. 그렇지 않으면 4바이트의 청크는 그 일부가 하나의 블록에 있고 다른 블록에 있을 수 있으므로 프로세서가 이 데이터를 읽기 위해 2개의 워드를 처리해야 합니다.
8바이트 더블에도 동일하게 적용됩니다.단, 블록 내에 항상 존재하도록 하려면 메모리주소 8의 배수 내에 있어야 합니다.
이것은 8바이트 워드프로세서를 고려하지만 이 개념은 다른 크기의 단어에 적용됩니다.
패딩은 이러한 데이터 사이의 간격을 메워 블록과 정렬되도록 함으로써 메모리를 읽을 때 성능을 향상시킵니다.
하지만, 다른 사람들의 답변에 있듯이, 때로는 성능 자체보다 공간이 더 중요할 때가 있습니다.RAM이 많지 않은 컴퓨터에서 많은 데이터를 처리하고 있을 수 있습니다(스왑 공간은 사용할 수 있지만 훨씬 느림).최소 패딩이 완료될 때까지 프로그램 내의 변수를 배열할 수 있지만(다른 답변에서 크게 설명되었듯이), 이것이 충분하지 않을 경우 패딩을 명시적으로 비활성화할 수 있습니다(패킹이 바로 패딩입니다.
변수는 정렬(일반적으로 크기에 따라)로 나눌 수 있는 모든 주소에 저장됩니다.따라서 패딩/패킹은 구조만을 위한 것이 아닙니다.실제로 모든 데이터에는 고유한 정렬 요건이 있습니다.
int main(void) {
// We assume the `c` is stored as first byte of machine word
// as a convenience! If the `c` was stored as a last byte of previous
// word, there is no need to pad bytes before variable `i`
// because `i` is automatically aligned in a new word.
char c; // starts from any addresses divisible by 1(any addresses).
char pad[3]; // not-used memory for `i` to start from its address.
int32_t i; // starts from any addresses divisible by 4.
이것은 구조와 비슷하지만 몇 가지 차이점이 있습니다.우선 패딩에는 2종류가 있다고 할 수 있습니다.a) 각 멤버를 주소에서 올바르게 시작하기 위해 멤버 사이에 바이트가 삽입됩니다.b) 주소에서 다음 구조 인스턴스를 올바르게 시작하기 위해 각 구조체에 바이트가 추가됩니다.
// Example for rule 1 below.
struct st {
char c; // starts from any addresses divisible by 4, not 1.
char pad[3]; // not-used memory for `i` to start from its address.
int32_t i; // starts from any addresses divisible by 4.
};
// Example for rule 2 below.
struct st {
int32_t i; // starts from any addresses divisible by 4.
char c; // starts from any addresses.
char pad[3]; // not-used memory for next `st`(or anything that has same
// alignment requirement) to start from its own address.
};
- 첫 멤버는 큰 얼라인먼트되는 구조에 의해 분할 됩니다(여기서는 가장 큰 멤버의 얼라인먼트 요건에 의해 결정됨).
4
의 , 「」int32_t
이것은 일반 변수와는 다릅니다.정규 변수는 정렬로 나눌 수 있는 모든 주소를 시작할 수 있지만 구조물의 첫 번째 멤버는 그렇지 않습니다.아시다시피 구조물의 주소는 첫 번째 멤버의 주소와 동일합니다. - 구조체 내부에 추가 패딩된 후행 바이트가 있을 수 있으며, 해당 주소에서 시작하는 다음 구조체(또는 구조체 배열의 다음 요소)를 만들 수 있습니다.
struct st arr[2];
만들기 위해서arr[1]
)arr[1]
의 첫 번째 멤버)는 4로 나누어진 주소에서 시작하여 각 구조체의 끝에 3바이트를 추가해야 합니다.
이것은 구조 포장 기술의 잃어버린 기술에서 배운 것입니다.
은, 「」를 참조해 ._Alignof
을 얻을 수 은 ㄴ, ㄴ ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ, ㄴ.offsetof
이 구조물은 패딩되어 있습니까, 아니면 패킹되어 있습니까?
패딩이 되어 있어요.
처음에 생각나는 유일한 가능성은, 포장할 수 있는 곳은,char
★★★★★★★★★★★★★★★★★」int
같기 것으로 되어 있습니다.char/int/char
에서는 패딩이 되지 않습니다.또, 「패딩」에 대해서도 입니다.int/char
★★★★★★ 。
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 모두 필요합니다.sizeof(int)
★★★★★★★★★★★★★★★★★」sizeof(char)
(12와 8 사이즈를 얻기 위해) 4가 되다모든 이론이 무너지는 것은 기준에 의해 보장되기 때문이다.sizeof(char)
항상 하나다.
char
★★★★★★★★★★★★★★★★★」int
같은 너비로 4와 4가 아니라 1과 1이 될 것입니다.따라서 12의 크기를 얻으려면 마지막 필드 뒤에 패딩이 있어야 합니다.
패딩 또는 패킹은 언제 이루어집니까?
컴파일러 실장이 원할 때마다.컴파일러는 필드 사이에 패딩을 삽입할 수 있으며 마지막 필드 뒤에 (첫 번째 필드 앞에는 삽입할 수 없습니다)
이는 일반적으로 특정 경계에 정렬되어 있을 때 성능이 향상되기 때문에 성능을 위해 수행됩니다.얼라인먼트되지 않은 데이터에 액세스하려고 하면 기능하지 않는 아키텍처도 있습니다(즉, 크래시).
패킹 끝)은으로 구현 할 수 를 들어, 「」의 「/」(「」의 「」)등의 기능이 있습니다.#pragma pack
특정 실장에서는 코드를 체크할 수 없는 경우에도 컴파일 시에 코드를 체크하여 요건을 충족하는지 확인할 수 있습니다(실장 고유의 것이 아니라 표준 C 기능을 사용합니다).
예를 들어 다음과 같습니다.
// C11 or better ...
#include <assert.h>
struct strA { char a; int b; char c; } x;
struct strB { int b; char a; } y;
static_assert(sizeof(struct strA) == sizeof(char)*2 + sizeof(int), "No padding allowed");
static_assert(sizeof(struct strB) == sizeof(char) + sizeof(int), "No padding allowed");
이러한 구조에 패딩이 있는 경우 이와 같은 것은 컴파일을 거부합니다.
구조 패킹은 구조 패딩, 정렬이 가장 중요할 때 사용되는 패킹, 공간이 가장 중요할 때 사용되는 패킹을 억제합니다.
「」가 제공됩니다.#pragma
패딩을 억제하거나 n바이트 수로 패킹합니다.이를 위한 키워드를 제공하는 사람도 있습니다.일반적으로 구조 패딩을 수정하기 위해 사용되는 플러그마는 다음과 같은 형식입니다(컴파일러에 따라 다름).
#pragma pack(n)
를 들어 은 'ARM'을 합니다.__packed
구조 패딩을 억제하는 키워드입니다.자세한 것은, 컴파일러의 메뉴얼을 참조해 주세요.
포장된 구조는 패딩이 없는 구조입니다.
일반적으로 포장된 구조물을 사용한다.
공간을 절약하다
를 포맷하는 이것은 . 이 하기 때문입니다).
★★★★★★★★★★★★★★★★★★★★★」
패딩 규칙:
- 구조체의 모든 구성원은 크기에 따라 나누어질 수 있는 주소에 있어야 합니다.패딩은 이 규칙이 충족되는지 확인하기 위해 요소 간 또는 구조물의 끝에 삽입됩니다.이는 하드웨어에 의한 보다 쉽고 효율적인 버스 액세스를 위해 이루어집니다.
- 구조물의 끝부분에서의 패딩은 구조물의 가장 큰 부재의 크기에 따라 결정된다.
규칙 2: 다음 구조를 고려하십시오.
이 구조의 어레이(2개의 구조)를 작성할 경우 마지막에 패딩이 필요하지 않습니다.
따라서 구조체의 크기는 8바이트입니다.= 8바이트
다음과 같이 다른 구조를 작성한다고 가정합니다.
이 구조의 배열을 작성할 경우 마지막에 필요한 패딩 바이트 수 중 두 가지 가능성이 있습니다.
A. 마지막에 3바이트를 추가하여 롱이 아닌 int로 정렬하는 경우:
B. 끝에 7바이트를 추가하고 Long에 맞춰 정렬하는 경우:
두 번째 어레이의 시작 주소는 8의 배수(24)입니다.구조체의 크기 = 24바이트
따라서 구조의 다음 배열의 시작 주소를 가장 큰 멤버의 배수(즉, 이 구조의 배열을 작성하는 경우)에 맞추면 두 번째 배열의 첫 번째 주소는 구조의 가장 큰 멤버의 배수인 주소로 시작해야 합니다.여기에서는 24(3*8)로 마지막에 필요한 패딩 바이트 수를 계산할 수 있습니다.
그것 말고는 다른 방법이 없어요!주제를 파악하고자 하는 사람은 다음과 같은 것을 해야 합니다.
- Eric S가 쓴 잃어버린 구조 포장 기술을 읽어보세요.레이먼드
- Eric의 코드 예시 보기
- 마지막으로 구조물이 가장 큰 유형의 선형 요건에 맞춰 정렬된다는 패딩에 대한 다음 규칙을 잊지 마십시오.
패딩과 패킹은 같은 것의 두 가지 측면일 뿐입니다.
- 패킹 또는 정렬은 각 부재가 반올림되는 크기이다
- padding은 정렬에 맞추기 위해 추가된 추가 공간입니다.
»mystruct_A
디폴트 얼라인먼트를 4로 하면 각 멤버는 4바이트의 배수로 정렬됩니다.★★★의 char
은 1로, 1은 입니다.a
★★★★★★★★★★★★★★★★★」c
는 4 - 1=3입니다만, 4 - 1 = 3 바만만 、 3 만만만 만 is만 만 、 패 is is is is is is is is is is is is 、 4 - 1 = 3 is is is is is is is is is 。int b
네 번째입니다.같은 방법으로 동작합니다.mystruct_B
.
데이터 구조 정렬은 컴퓨터 메모리에서 데이터를 정렬하고 액세스하는 방법입니다.데이터 정렬과 데이터 구조 패딩의 두 가지 개별적인 문제로 구성됩니다.최신 컴퓨터는 메모리 주소에서 읽거나 메모리에 쓸 때 워드 크기 청크(32비트 시스템의 경우 4바이트 청크 등) 이상의 청크로 이를 수행합니다.데이터 정렬이란 데이터를 워드 크기의 몇 배와 동일한 메모리 주소에 배치하는 것을 의미하며, CPU가 메모리를 처리하는 방식에 따라 시스템의 성능이 향상됩니다.데이터를 정렬하려면 마지막 데이터 구조의 끝과 다음 데이터 구조의 시작(데이터 구조 패딩) 사이에 의미 없는 바이트를 삽입해야 할 수 있습니다.
- 메모리내의 데이터를 정렬하기 위해서, 메모리 할당중에 다른 구조 멤버에 할당되는 메모리 주소간에 1개 또는 복수의 빈 바이트(주소)를 삽입(또는 빈 채로 둔다)한다.이 개념은 구조 패딩이라고 불립니다.
- 컴퓨터 프로세서의 아키텍처는 메모리에서 한 번에 한 단어(32비트 프로세서로 4바이트)를 읽을 수 있는 방법입니다.
- 프로세서의 이점을 활용하기 위해 데이터는 항상 4바이트 패키지로 정렬되어 있으며, 이로 인해 다른 멤버의 주소 사이에 빈 주소가 삽입됩니다.
- C의 이 구조 패딩 개념 때문에 구조의 크기가 항상 우리가 생각하는 것과 같지 않습니다.
구조 패킹은 컴파일러에 구조 패킹을 명시적으로 지시했을 때만 실행됩니다.패딩이 보이는 거예요32비트 시스템은 각 필드를 워드 정렬로 채웁니다.컴파일러에게 구조를 포장하라고 지시했다면 각각 6바이트와 5바이트가 될 것입니다.하지만 그러지 마세요.이것은 휴대성이 없고 컴파일러의 코드 생성 속도가 훨씬 느립니다(때로는 버그가 발생할 수도 있습니다).
언급URL : https://stackoverflow.com/questions/4306186/structure-padding-and-packing
'IT이야기' 카테고리의 다른 글
테스트에서 vue 계산 속성을 수동으로 업데이트하는 방법 (0) | 2022.06.25 |
---|---|
vue js 팝업에서 카운트다운 타이머가 작동하지 않음 (0) | 2022.06.25 |
-fPIC 컴파일러 옵션을 추가하는 CMAKE의 관용적인 방법은 무엇입니까? (0) | 2022.06.22 |
vue js 2 테이블 정렬 (0) | 2022.06.22 |
.a 및 .so 파일이란? (0) | 2022.06.22 |