program story

구조체의 sizeof가 각 멤버의 sizeof 합계와 같지 않은 이유는 무엇입니까?

inputbox 2020. 10. 2. 22:12
반응형

구조체의 sizeof가 각 멤버의 sizeof 합계와 같지 않은 이유는 무엇입니까?


sizeof연산자가 구조 멤버의 총 크기보다 큰 구조의 크기를 반환하는 이유는 무엇 입니까?


이는 정렬 제약 조건을 충족하기 위해 추가 된 패딩 때문입니다. 데이터 구조 정렬 은 프로그램의 성능과 정확성 모두에 영향을 미칩니다.

  • 잘못 정렬 된 액세스는 하드 오류 일 수 있습니다 (종종 SIGBUS).
  • 잘못 정렬 된 액세스는 소프트 오류 일 수 있습니다.
    • 적당한 성능 저하를 위해 하드웨어에서 수정되었습니다.
    • 또는 심각한 성능 저하를 위해 소프트웨어의 에뮬레이션으로 수정되었습니다.
    • 또한 원 자성 및 기타 동시성 보장이 손상되어 미묘한 오류가 발생할 수 있습니다.

다음은 x86 프로세서 (모두 32 비트 및 64 비트 모드 사용)에 대한 일반 설정을 사용하는 예입니다.

struct X
{
    short s; /* 2 bytes */
             /* 2 padding bytes */
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 3 padding bytes */
};

struct Y
{
    int   i; /* 4 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
    short s; /* 2 bytes */
};

struct Z
{
    int   i; /* 4 bytes */
    short s; /* 2 bytes */
    char  c; /* 1 byte */
             /* 1 padding byte */
};

const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */

정렬별로 멤버를 정렬하여 구조의 크기를 최소화 할 수 있습니다 ( Z위 예제의 구조와 같이 기본 유형의 경우 크기별 정렬로 충분 함 ).

중요 참고 : C 및 C ++ 표준은 모두 구조 정렬이 구현에 정의되어 있다고 명시합니다. 따라서 각 컴파일러는 데이터를 다르게 정렬하도록 선택하여 서로 다른 호환되지 않는 데이터 레이아웃을 만들 수 있습니다. 따라서 여러 컴파일러에서 사용할 라이브러리를 다룰 때 컴파일러가 데이터를 정렬하는 방법을 이해하는 것이 중요합니다. 일부 컴파일러에는 #pragma구조 정렬 설정을 변경하는 명령 줄 설정 및 / 또는 특수 문이 있습니다.


패킹 및 바이트 정렬은 FAQ C에 설명 된대로 여기서 :

정렬을위한 것입니다. 많은 프로세서가 모든 방식으로 압축되어있는 경우 2 바이트 및 4 바이트 수량 (예 : int 및 long int)에 액세스 할 수 없습니다.

다음과 같은 구조가 있다고 가정합니다.

struct {
    char a[3];
    short int b;
    long int c;
    char d[3];
};

이제이 구조를 다음과 같이 메모리에 압축하는 것이 가능해야한다고 생각할 수 있습니다.

+-------+-------+-------+-------+
|           a           |   b   |
+-------+-------+-------+-------+
|   b   |           c           |
+-------+-------+-------+-------+
|   c   |           d           |
+-------+-------+-------+-------+

그러나 컴파일러가 다음과 같이 정렬하면 프로세서에서 훨씬 더 쉽습니다.

+-------+-------+-------+
|           a           |
+-------+-------+-------+
|       b       |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           |
+-------+-------+-------+

압축 된 버전에서 b 및 c 필드가 어떻게 둘러싸여 있는지 확인하는 것이 당신과 나에게 얼마나 어려운지 확인하십시오. 요컨대, 프로세서도 어렵습니다. 따라서 대부분의 컴파일러는 다음과 같이 구조를 채 웁니다 (보이지 않는 추가 필드가있는 것처럼).

+-------+-------+-------+-------+
|           a           | pad1  |
+-------+-------+-------+-------+
|       b       |     pad2      |
+-------+-------+-------+-------+
|               c               |
+-------+-------+-------+-------+
|           d           | pad3  |
+-------+-------+-------+-------+

예를 들어 GCC에서 구조가 특정 크기를 갖기를 원하면 __attribute__((packed)).

On Windows you can set the alignment to one byte when using the cl.exe compier with the /Zp option.

Usually it is easier for the CPU to access data that is a multiple of 4 (or 8), depending platform and also on the compiler.

So it is a matter of alignment basically.

You need to have good reasons to change it.


This can be due to byte alignment and padding so that the structure comes out to an even number of bytes (or words) on your platform. For example in C on Linux, the following 3 structures:

#include "stdio.h"


struct oneInt {
  int x;
};

struct twoInts {
  int x;
  int y;
};

struct someBits {
  int x:2;
  int y:6;
};


int main (int argc, char** argv) {
  printf("oneInt=%zu\n",sizeof(struct oneInt));
  printf("twoInts=%zu\n",sizeof(struct twoInts));
  printf("someBits=%zu\n",sizeof(struct someBits));
  return 0;
}

Have members who's sizes (in bytes) are 4 bytes (32 bits), 8 bytes (2x 32 bits) and 1 byte (2+6 bits) respectively. The above program (on Linux using gcc) prints the sizes as 4, 8, and 4 - where the last structure is padded so that it is a single word (4 x 8 bit bytes on my 32bit platform).

oneInt=4
twoInts=8
someBits=4

See also:

for Microsoft Visual C:

http://msdn.microsoft.com/en-us/library/2e70t5y1%28v=vs.80%29.aspx

and GCC claim compatibility with Microsoft's compiler.:

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

In addition to the previous answers, please note that regardless the packaging, there is no members-order-guarantee in C++. Compilers may (and certainly do) add virtual table pointer and base structures' members to the structure. Even the existence of virtual table is not ensured by the standard (virtual mechanism implementation is not specified) and therefore one can conclude that such guarantee is just impossible.

I'm quite sure member-order is guaranteed in C, but I wouldn't count on it, when writing a cross-platform or cross-compiler program.


The size of a structure is greater than the sum of its parts because of what is called packing. A particular processor has a preferred data size that it works with. Most modern processors' preferred size if 32-bits (4 bytes). Accessing the memory when data is on this kind of boundary is more efficient than things that straddle that size boundary.

For example. Consider the simple structure:

struct myStruct
{
   int a;
   char b;
   int c;
} data;

If the machine is a 32-bit machine and data is aligned on a 32-bit boundary, we see an immediate problem (assuming no structure alignment). In this example, let us assume that the structure data starts at address 1024 (0x400 - note that the lowest 2 bits are zero, so the data is aligned to a 32-bit boundary). The access to data.a will work fine because it starts on a boundary - 0x400. The access to data.b will also work fine, because it is at address 0x404 - another 32-bit boundary. But an unaligned structure would put data.c at address 0x405. The 4 bytes of data.c are at 0x405, 0x406, 0x407, 0x408. On a 32-bit machine, the system would read data.c during one memory cycle, but would only get 3 of the 4 bytes (the 4th byte is on the next boundary). So, the system would have to do a second memory access to get the 4th byte,

Now, if instead of putting data.c at address 0x405, the compiler padded the structure by 3 bytes and put data.c at address 0x408, then the system would only need 1 cycle to read the data, cutting access time to that data element by 50%. Padding swaps memory efficiency for processing efficiency. Given that computers can have huge amounts of memory (many gigabytes), the compilers feel that the swap (speed over size) is a reasonable one.

Unfortunately, this problem becomes a killer when you attempt to send structures over a network or even write the binary data to a binary file. The padding inserted between elements of a structure or class can disrupt the data sent to the file or network. In order to write portable code (one that will go to several different compilers), you will probably have to access each element of the structure separately to ensure the proper "packing".

On the other hand, different compilers have different abilities to manage data structure packing. For example, in Visual C/C++ the compiler supports the #pragma pack command. This will allow you to adjust data packing and alignment.

For example:

#pragma pack 1
struct MyStruct
{
    int a;
    char b;
    int c;
    short d;
} myData;

I = sizeof(myData);

I should now have the length of 11. Without the pragma, I could be anything from 11 to 14 (and for some systems, as much as 32), depending on the default packing of the compiler.


It can do so if you have implicitly or explicitly set the alignment of the struct. A struct that is aligned 4 will always be a multiple of 4 bytes even if the size of its members would be something that's not a multiple of 4 bytes.

Also a library may be compiled under x86 with 32-bit ints and you may be comparing its components on a 64-bit process would would give you a different result if you were doing this by hand.


C99 N1256 standard draft

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

6.5.3.4 The sizeof operator:

3 When applied to an operand that has structure or union type, the result is the total number of bytes in such an object, including internal and trailing padding.

6.7.2.1 Structure and union specifiers:

13 ... There may be unnamed padding within a structure object, but not at its beginning.

and:

15 There may be unnamed padding at the end of a structure or union.

The new C99 flexible array member feature (struct S {int is[];};) may also affect padding:

16 As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

Annex J Portability Issues reiterates:

The following are unspecified: ...

  • The value of padding bytes when storing values in structures or unions (6.2.6.1)

C++11 N3337 standard draft

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

5.3.3 Sizeof:

2 When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array.

9.2 Class members:

A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. — end note ]

I only know enough C++ to understand the note :-)


In addition to the other answers, a struct can (but usually doesn't) have virtual functions, in which case the size of the struct will also include the space for the vtbl.


C language leaves compiler some freedom about the location of the structural elements in the memory:

  • memory holes may appear between any two components, and after the last component. It was due to the fact that certain types of objects on the target computer may be limited by the boundaries of addressing
  • "memory holes" size included in the result of sizeof operator. The sizeof only doesn't include size of the flexible array, which is available in C/C++
  • Some implementations of the language allow you to control the memory layout of structures through the pragma and compiler options

The C language provides some assurance to the programmer of the elements layout in the structure:

  • compilers required to assign a sequence of components increasing memory addresses
  • Address of the first component coincides with the start address of the structure
  • unnamed bit fields may be included in the structure to the required address alignments of adjacent elements

Problems related to the elements alignment:

  • Different computers line the edges of objects in different ways
  • Different restrictions on the width of the bit field
  • Computers differ on how to store the bytes in a word (Intel 80x86 and Motorola 68000)

How alignment works:

  • The volume occupied by the structure is calculated as the size of the aligned single element of an array of such structures. The structure should end so that the first element of the next following structure does not the violate requirements of alignment

p.s More detailed info are available here: "Samuel P.Harbison, Guy L.Steele C A Reference, (5.6.2 - 5.6.7)"


The idea is that for speed and cache considerations, operands should be read from addresses aligned to their natural size. To make this happen, the compiler pads structure members so the following member or following struct will be aligned.

struct pixel {
    unsigned char red;   // 0
    unsigned char green; // 1
    unsigned int alpha;  // 4 (gotta skip to an aligned offset)
    unsigned char blue;  // 8 (then skip 9 10 11)
};

// next offset: 12

The x86 architecture has always been able to fetch misaligned addresses. However, it's slower and when the misalignment overlaps two different cache lines, then it evicts two cache lines when an aligned access would only evict one.

Some architectures actually have to trap on misaligned reads and writes, and early versions of the ARM architecture (the one that evolved into all of today's mobile CPUs) ... well, they actually just returned bad data on for those. (They ignored the low-order bits.)

Finally, note that cache lines can be arbitrarily large, and the compiler doesn't attempt to guess at those or make a space-vs-speed tradeoff. Instead, the alignment decisions are part of the ABI and represent the minimum alignment that will eventually evenly fill up a cache line.

TL;DR: alignment is important.

참고URL : https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member

반응형