Tại sao sizeof cho một cấu trúc bằng tổng sizeof của mỗi thành viên?


697

Tại sao sizeoftoán tử trả về kích thước lớn hơn cho cấu trúc so với tổng kích thước của các thành viên của cấu trúc?


14
Xem C FAQ này về bộ nhớ tăng cường. c-faq.com/struct/align.esr.html
Richard Chambers

48
Giai thoại: Có một loại virus máy tính thực sự đặt mã của nó trong phần đệm cấu trúc trong chương trình máy chủ.
Elazar

4
@Elazar Thật ấn tượng! Tôi sẽ không bao giờ nghĩ rằng có thể sử dụng các khu vực nhỏ như vậy cho bất cứ điều gì. Bạn có thể cung cấp thêm chi tiết?
Wilson

1
@Wilson - Tôi chắc chắn rằng nó liên quan đến rất nhiều jmp.
hoodaticus

4
Xem phần đệm cấu trúc , đóng gói : Nghệ thuật mất cấu trúc C Đóng gói Eric S. Raymond
EsmaeelE

Câu trả lời:


649

Điều này là do phần đệm được thêm vào để đáp ứng các ràng buộc căn chỉnh. Căn chỉnh cấu trúc dữ liệu ảnh hưởng đến cả hiệu suất và tính chính xác của các chương trình:

  • Truy cập sai liên kết có thể là một lỗi cứng (thường SIGBUS).
  • Truy cập sai liên kết có thể là một lỗi mềm.
    • Hoặc được sửa chữa trong phần cứng, cho một sự suy giảm hiệu suất khiêm tốn.
    • Hoặc sửa chữa bằng cách mô phỏng trong phần mềm, cho sự suy giảm hiệu năng nghiêm trọng.
    • Ngoài ra, tính nguyên tử và các đảm bảo đồng thời khác có thể bị phá vỡ, dẫn đến các lỗi tinh vi.

Dưới đây là một ví dụ sử dụng các cài đặt điển hình cho bộ xử lý x86 (tất cả các chế độ 32 và 64 bit được sử dụng):

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 */

Người ta có thể giảm thiểu kích thước của các cấu trúc bằng cách sắp xếp các thành viên bằng cách căn chỉnh (sắp xếp theo đủ kích thước cho các kiểu cơ bản) (giống như cấu trúc Ztrong ví dụ trên).

LƯU Ý QUAN TRỌNG: Cả hai tiêu chuẩn C và C ++ đều nói rằng sự liên kết cấu trúc được xác định theo thực hiện. Do đó, mỗi trình biên dịch có thể chọn căn chỉnh dữ liệu khác nhau, dẫn đến bố cục dữ liệu khác nhau và không tương thích. Vì lý do này, khi làm việc với các thư viện sẽ được sử dụng bởi các trình biên dịch khác nhau, điều quan trọng là phải hiểu cách trình biên dịch sắp xếp dữ liệu. Một số trình biên dịch có cài đặt dòng lệnh và / hoặc các #pragmacâu lệnh đặc biệt để thay đổi cài đặt căn chỉnh cấu trúc.


38
Tôi muốn ghi chú ở đây: Hầu hết các bộ xử lý phạt bạn vì truy cập bộ nhớ không được phân bổ (như bạn đã đề cập), nhưng bạn không thể quên rằng nhiều người hoàn toàn không cho phép điều đó. Hầu hết các chip MIPS, đặc biệt, sẽ đưa ra một ngoại lệ đối với quyền truy cập không được phân bổ.
Cody Brocons

35
Các chip x86 thực sự khá độc đáo ở chỗ chúng cho phép truy cập không được phân bổ, mặc dù bị phạt; AFAIK hầu hết các chip sẽ ném ngoại lệ, không chỉ một vài. PowerPC là một ví dụ phổ biến khác.
Dark Shikari

6
Việc kích hoạt các pragma cho các truy cập không được sắp xếp thường làm cho mã của bạn tăng kích thước, trên các bộ xử lý gây ra lỗi sai, vì mã để khắc phục mọi sai lệch phải được tạo. ARM cũng ném lỗi sai.
Mike Dimmick

5
@Dark - Hoàn toàn đồng ý. Nhưng hầu hết các bộ xử lý máy tính để bàn là x86 / x64, vì vậy hầu hết các chip không phát sinh lỗi căn chỉnh dữ liệu;)
Aaron

27
Truy cập dữ liệu không được phân bổ thường là một tính năng được tìm thấy trong các kiến ​​trúc CISC và hầu hết các kiến ​​trúc RISC không bao gồm nó (ARM, MIPS, PowerPC, Cell). Trên thực tế, hầu hết các chip KHÔNG phải là bộ xử lý máy tính để bàn, cho quy tắc nhúng theo số lượng chip và phần lớn trong số này là các kiến ​​trúc RISC.
Lara Dougan

191

Đóng gói và căn chỉnh byte, như được mô tả trong C FAQ ở đây :

Đó là sự liên kết. Nhiều bộ xử lý không thể truy cập số lượng 2 và 4 byte (ví dụ: int và int dài) nếu chúng bị nhồi nhét mọi lúc.

Giả sử bạn có cấu trúc này:

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

Bây giờ, bạn có thể nghĩ rằng cần phải đóng gói cấu trúc này vào bộ nhớ như thế này:

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

Nhưng nó rất dễ dàng hơn nhiều trên bộ xử lý nếu trình biên dịch sắp xếp nó như thế này:

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

Trong phiên bản đóng gói, hãy chú ý làm thế nào ít nhất một chút khó khăn cho bạn và tôi để xem các trường b và c bao quanh như thế nào? Tóm lại, nó cũng khó cho bộ xử lý. Do đó, hầu hết các trình biên dịch sẽ đệm cấu trúc (như thể có thêm các trường vô hình) như thế này:

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

1
Bây giờ việc sử dụng các khe cắm bộ nhớ pad1, pad2 và pad3 là gì.
Lakshmi Saletanth Chitla


@EmmEff điều này có thể sai nhưng tôi không hiểu lắm: tại sao không có khe nhớ cho con trỏ trong mảng?
Balázs Börcsök

1
@ BalázsBörcsök Đây là các mảng có kích thước không đổi, và do đó các phần tử của chúng được lưu trữ trực tiếp trong cấu trúc tại các độ lệch cố định. Trình biên dịch biết tất cả điều này tại thời gian biên dịch nên con trỏ ẩn. Ví dụ, nếu bạn có một biến cấu trúc của loại này được gọi là ssau đó &s.a == &s&s.d == &s + 12(đưa ra sự liên kết được hiển thị trong câu trả lời). Con trỏ chỉ được lưu trữ nếu các mảng có kích thước thay đổi (ví dụ: ađược khai báo char a[]thay vì char a[3]), nhưng sau đó các phần tử phải được lưu trữ ở nơi khác.
kbolino

27

Nếu bạn muốn cấu trúc có kích thước nhất định với GCC chẳng hạn, hãy sử dụng __attribute__((packed)).

Trên Windows, bạn có thể đặt căn chỉnh thành một byte khi sử dụng trình biên dịch cl.exe với tùy chọn / Zp .

Thông thường, CPU sẽ dễ dàng truy cập dữ liệu hơn bội số của 4 (hoặc 8), tùy thuộc vào nền tảng và cả trình biên dịch.

Vì vậy, nó là một vấn đề liên kết về cơ bản.

Bạn cần có lý do chính đáng để thay đổi nó.


5
"Lý do chính đáng" Ví dụ: Giữ tính tương thích nhị phân (phần đệm) nhất quán giữa các hệ thống 32 bit và 64 bit cho một cấu trúc phức tạp trong mã trình diễn bằng chứng khái niệm sẽ được trình chiếu vào ngày mai. Đôi khi sự cần thiết phải được ưu tiên hơn quyền sở hữu.
Mr.Ree

2
Mọi thứ đều ổn trừ khi bạn đề cập đến Hệ điều hành. Đây là một vấn đề về tốc độ CPU, hệ điều hành hoàn toàn không liên quan.
Blaisorblade

3
Một lý do chính đáng khác là nếu bạn nhồi dữ liệu vào một cấu trúc, ví dụ như khi phân tích cú pháp các giao thức mạng.
ceo

1
@dolmen Tôi chỉ chỉ ra rằng "Hệ thống Operatin dễ dàng truy cập dữ liệu hơn" là không chính xác, vì HĐH không truy cập dữ liệu.
Blaisorblade

1
@dolmen Trên thực tế, người ta nên nói về ABI (giao diện nhị phân ứng dụng). Căn chỉnh mặc định (được sử dụng nếu bạn không thay đổi nó trong nguồn) phụ thuộc vào ABI và nhiều HĐH hỗ trợ nhiều ABI (giả sử, 32 và 64 bit hoặc cho các nhị phân từ các HĐH khác nhau hoặc cho các cách biên dịch khác nhau cùng nhị phân cho cùng một hệ điều hành). OTOH, việc căn chỉnh nào là hiệu quả, thuận tiện tùy thuộc vào CPU - bộ nhớ được truy cập theo cùng một cách cho dù bạn sử dụng chế độ 32 hay 64 bit (hiện tại tôi không thể nhận xét về chế độ thực, nhưng dường như hầu như không liên quan đến hiệu suất hiện nay). Pentium IIRC bắt đầu thích căn chỉnh 8 byte.
Blaisorblade

15

Điều này có thể là do căn chỉnh byte và phần đệm để cấu trúc đi ra một số byte (hoặc từ) chẵn trên nền tảng của bạn. Ví dụ trong C trên Linux, 3 cấu trúc sau:

#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;
}

Có các thành viên có kích thước (tính bằng byte) lần lượt là 4 byte (32 bit), 8 byte (2x 32 bit) và 1 byte (2 + 6 bit). Chương trình trên (trên Linux sử dụng gcc) in các kích thước là 4, 8 và 4 - trong đó cấu trúc cuối cùng được đệm sao cho nó là một từ đơn (4 x 8 bit byte trên nền tảng 32 bit của tôi).

oneInt=4
twoInts=8
someBits=4

4
"C trên Linux sử dụng gcc" là không đủ để mô tả nền tảng của bạn. Sự sắp xếp chủ yếu phụ thuộc vào kiến ​​trúc CPU.
heo

- @ Kyle Burton. Xin lỗi, tôi không hiểu tại sao kích thước của cấu trúc "someBits" bằng 4, tôi mong đợi 8 byte vì có 2 số nguyên được khai báo (2 * sizeof (int)) = 8 byte. cảm ơn
youpilat13

1
Xin chào @ youpilat13, :2:6thực sự chỉ định 2 và 6 bit, không phải là số nguyên 32 bit đầy đủ trong trường hợp này. someBits.x, chỉ có 2 bit chỉ có thể lưu trữ 4 giá trị có thể: 00, 01, 10 và 11 (1, 2, 3 và 4). Điều này có nghĩa không? Đây là một bài viết về tính năng: geekforgeek.org/bit-fields-c
Kyle Burton

11

Xem thêm:

cho Microsoft Visual C:

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

và yêu cầu tương thích GCC với trình biên dịch của Microsoft.:

http://gcc.gnu.org/onlinesocs/gcc/Str struct_002dPacking-Pragmas.html

Ngoài các câu trả lời trước, xin lưu ý rằng bất kể bao bì, không có bảo đảm đặt hàng thành viên trong C ++ . Trình biên dịch có thể (và chắc chắn làm) thêm con trỏ bảng ảo và các thành viên cấu trúc cơ sở vào cấu trúc. Ngay cả sự tồn tại của bảng ảo cũng không được đảm bảo theo tiêu chuẩn (việc thực hiện cơ chế ảo không được chỉ định) và do đó người ta có thể kết luận rằng bảo đảm đó là không thể.

Tôi khá chắc chắn rằng thứ tự thành viên được đảm bảo bằng C , nhưng tôi sẽ không tin vào điều đó, khi viết chương trình đa nền tảng hoặc trình biên dịch chéo.


4
"Tôi khá chắc chắn rằng thứ tự thành viên bị càu nhàu trong C". Có, C99 nói: "Trong một đối tượng cấu trúc, các thành viên không phải là bit và các đơn vị trong đó các trường bit cư trú có địa chỉ tăng theo thứ tự mà chúng được khai báo." Tiêu chuẩn tốt hơn tại: stackoverflow.com/a/37032302/895245
Ciro Santilli 病毒 审查 六四 事件


8

Kích thước của một cấu trúc lớn hơn tổng của các bộ phận của nó vì những gì được gọi là đóng gói. Một bộ xử lý cụ thể có kích thước dữ liệu ưa thích mà nó hoạt động. Kích thước ưa thích của hầu hết các bộ xử lý hiện đại nếu 32 bit (4 byte). Truy cập bộ nhớ khi dữ liệu nằm trên loại ranh giới này hiệu quả hơn so với những thứ nằm trên ranh giới kích thước đó.

Ví dụ. Hãy xem xét cấu trúc đơn giản:

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

Nếu máy là máy 32 bit và dữ liệu được căn chỉnh trên ranh giới 32 bit, chúng ta sẽ thấy một vấn đề ngay lập tức (giả sử không có sự liên kết cấu trúc). Trong ví dụ này, chúng ta giả sử rằng dữ liệu cấu trúc bắt đầu tại địa chỉ 1024 (0x400 - lưu ý rằng 2 bit thấp nhất bằng 0, do đó dữ liệu được căn chỉnh theo ranh giới 32 bit). Quyền truy cập vào data.a sẽ hoạt động tốt vì nó bắt đầu trên một ranh giới - 0x400. Quyền truy cập vào data.b cũng sẽ hoạt động tốt, vì nó ở địa chỉ 0x404 - một ranh giới 32 bit khác. Nhưng một cấu trúc không được sắp xếp sẽ đặt data.c tại địa chỉ 0x405. 4 byte dữ liệu.c là 0x405, 0x406, 0x407, 0x408. Trên máy 32 bit, hệ thống sẽ đọc data.c trong một chu kỳ bộ nhớ, nhưng sẽ chỉ nhận được 3 trong số 4 byte (byte thứ 4 nằm ở ranh giới tiếp theo). Vì vậy, hệ thống sẽ phải thực hiện truy cập bộ nhớ thứ hai để có được byte thứ 4,

Bây giờ, nếu thay vì đặt data.c tại địa chỉ 0x405, trình biên dịch đã đệm cấu trúc 3 byte và đặt data.c tại địa chỉ 0x408, thì hệ thống sẽ chỉ cần 1 chu kỳ để đọc dữ liệu, giảm thời gian truy cập vào phần tử dữ liệu đó bằng 50%. Đệm đổi hiệu quả bộ nhớ cho hiệu quả xử lý. Cho rằng máy tính có thể có bộ nhớ lớn (nhiều gigabyte), trình biên dịch cảm thấy rằng hoán đổi (tốc độ trên kích thước) là hợp lý.

Thật không may, vấn đề này trở thành một kẻ giết người khi bạn cố gắng gửi các cấu trúc qua mạng hoặc thậm chí ghi dữ liệu nhị phân vào một tệp nhị phân. Phần đệm được chèn giữa các thành phần của cấu trúc hoặc lớp có thể phá vỡ dữ liệu được gửi đến tệp hoặc mạng. Để viết mã di động (một mã sẽ đi đến một số trình biên dịch khác nhau), bạn có thể sẽ phải truy cập từng phần tử của cấu trúc một cách riêng biệt để đảm bảo "đóng gói" thích hợp.

Mặt khác, các trình biên dịch khác nhau có khả năng khác nhau để quản lý việc đóng gói cấu trúc dữ liệu. Ví dụ, trong Visual C / C ++, trình biên dịch hỗ trợ lệnh gói #pragma. Điều này sẽ cho phép bạn điều chỉnh đóng gói dữ liệu và căn chỉnh.

Ví dụ:

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

I = sizeof(myData);

Bây giờ tôi sẽ có độ dài 11. Không có pragma, tôi có thể là bất cứ thứ gì từ 11 đến 14 (và đối với một số hệ thống, nhiều như 32), tùy thuộc vào cách đóng gói mặc định của trình biên dịch.


Điều này thảo luận về hậu quả của đệm cấu trúc, nhưng nó không trả lời câu hỏi.
Keith Thompson

" ... bởi vì cái được gọi là đóng gói. ... - Tôi nghĩ bạn có nghĩa là" đệm "." Kích thước ưa thích của bộ xử lý hiện đại nhất nếu 32 bit (4 byte) "- Đó là một chút quá mức. kích thước 8, 16, 32 và 64 bit được hỗ trợ, thường thì mỗi kích thước có sự liên kết riêng. Và tôi không chắc câu trả lời của bạn có thêm bất kỳ thông tin mới nào chưa có trong câu trả lời được chấp nhận.
Keith Thompson

1
Khi tôi nói đóng gói, tôi có nghĩa là cách trình biên dịch đóng gói dữ liệu vào một cấu trúc (và nó có thể làm như vậy bằng cách đệm các mục nhỏ, nhưng nó không cần phải đệm, nhưng nó luôn đóng gói). Về kích thước - tôi đã nói về kiến ​​trúc hệ thống, không phải hệ thống sẽ hỗ trợ gì cho việc truy cập dữ liệu (khác với kiến ​​trúc xe buýt cơ bản). Đối với bình luận cuối cùng của bạn, tôi đã đưa ra một lời giải thích đơn giản và mở rộng về một khía cạnh của sự đánh đổi (tốc độ so với kích thước) - một vấn đề lập trình lớn. Tôi cũng mô tả một cách để khắc phục vấn đề - đó không phải là câu trả lời được chấp nhận.
sid1138

"Đóng gói" trong ngữ cảnh này thường đề cập đến việc phân bổ các thành viên chặt chẽ hơn so với mặc định, như với #pragma pack. Nếu các thành viên được phân bổ trên căn chỉnh mặc định của họ, tôi thường nói rằng cấu trúc không được đóng gói.
Keith Thompson

Đóng gói là một loại quá hạn. Nó có nghĩa là cách bạn đặt các yếu tố cấu trúc vào bộ nhớ. Tương tự như ý nghĩa của việc đặt các đối tượng vào một hộp (đóng gói để di chuyển). Nó cũng có nghĩa là đưa các phần tử vào bộ nhớ không có phần đệm (loại tay ngắn để "đóng gói chặt chẽ"). Sau đó, có phiên bản lệnh của từ trong lệnh gói #pragma.
sid1138

5

Nó có thể làm như vậy nếu bạn đã đặt ngầm định hoặc rõ ràng sự liên kết của cấu trúc. Một cấu trúc được căn chỉnh 4 sẽ luôn là bội số của 4 byte ngay cả khi kích thước của các thành viên của nó sẽ là một cái gì đó không phải là bội số của 4 byte.

Ngoài ra, một thư viện có thể được biên dịch dưới x86 với int 32 bit và bạn có thể so sánh các thành phần của nó trên quy trình 64 bit sẽ cho bạn một kết quả khác nếu bạn làm điều này bằng tay.


5

Dự thảo tiêu chuẩn C99 N1256

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

6.5.3.4 Toán tử sizeof :

3 Khi được áp dụng cho toán hạng có cấu trúc hoặc kiểu kết hợp, kết quả là tổng số byte trong một đối tượng như vậy, bao gồm cả phần đệm bên trong và dấu.

6.7.2.1 Cấu trúc và công cụ xác định liên minh :

13 ... Có thể có phần đệm không tên trong một đối tượng cấu trúc, nhưng không phải lúc bắt đầu.

và:

15 Có thể có phần đệm không tên ở cuối cấu trúc hoặc kết hợp.

Tính năng thành viên mảng linh hoạt C99 mới ( struct S {int is[];};) cũng có thể ảnh hưởng đến phần đệm:

16 Trong trường hợp đặc biệt, phần tử cuối cùng của cấu trúc có nhiều thành viên được đặt tên có thể có kiểu mảng không hoàn chỉnh; đây được gọi là thành viên mảng linh hoạt. Trong hầu hết các tình huống, thành viên mảng linh hoạt bị bỏ qua. Cụ thể, kích thước của cấu trúc như thể thành viên mảng linh hoạt bị bỏ qua ngoại trừ việc nó có thể có nhiều phần đệm hơn so với thiếu sót sẽ ngụ ý.

Phụ lục J Các vấn đề về tính di động nhắc lại:

Sau đây là không xác định: ...

  • Giá trị của byte đệm khi lưu trữ giá trị trong cấu trúc hoặc kết hợp (6.2.6.1)

Dự thảo tiêu chuẩn C ++ 11 N3337

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

5.3.3 Kích thước của :

2 Khi được áp dụng cho một lớp, kết quả là số byte trong một đối tượng của lớp đó bao gồm mọi phần đệm cần thiết để đặt các đối tượng của loại đó trong một mảng.

9.2 thành viên lớp :

Một con trỏ tới một đối tượng cấu trúc bố cục tiêu chuẩn, được chuyển đổi phù hợp bằng cách sử dụng reinterpret_cast, trỏ đến thành viên ban đầu của nó (hoặc nếu thành viên đó là một trường bit, sau đó đến đơn vị mà nó cư trú) và ngược lại. [Lưu ý: Do đó, có thể có phần đệm không tên trong một đối tượng cấu trúc bố cục tiêu chuẩn, nhưng không phải lúc đầu, khi cần thiết để đạt được sự liên kết thích hợp. - lưu ý cuối]

Tôi chỉ biết đủ C ++ để hiểu ghi chú :-)


4

Ngoài các câu trả lời khác, một cấu trúc có thể (nhưng thường không) có các hàm ảo, trong trường hợp đó kích thước của cấu trúc cũng sẽ bao gồm không gian cho vtbl.


8
Không hẳn. Trong các triển khai điển hình, những gì được thêm vào struct là một con trỏ vtable .
Don Wakefield

3

Ngôn ngữ C để lại cho trình biên dịch một số tự do về vị trí của các thành phần cấu trúc trong bộ nhớ:

  • lỗ nhớ có thể xuất hiện giữa hai thành phần bất kỳ và sau thành phần cuối cùng. Đó là do thực tế là một số loại đối tượng trên máy tính mục tiêu có thể bị giới hạn bởi các ranh giới của địa chỉ
  • Kích thước "lỗ nhớ" bao gồm trong kết quả của toán tử sizeof. Sizeof chỉ không bao gồm kích thước của mảng linh hoạt, có sẵn trong C / C ++
  • Một số triển khai ngôn ngữ cho phép bạn kiểm soát bố cục bộ nhớ của các cấu trúc thông qua các tùy chọn pragma và trình biên dịch

Ngôn ngữ C cung cấp một số đảm bảo cho lập trình viên về bố cục các thành phần trong cấu trúc:

  • trình biên dịch cần thiết để gán một chuỗi các thành phần tăng địa chỉ bộ nhớ
  • Địa chỉ của thành phần đầu tiên trùng với địa chỉ bắt đầu của cấu trúc
  • Các trường bit không tên có thể được bao gồm trong cấu trúc để sắp xếp địa chỉ yêu cầu của các phần tử liền kề

Các vấn đề liên quan đến sự liên kết các yếu tố:

  • Các máy tính khác nhau xếp các cạnh của các đối tượng theo những cách khác nhau
  • Các hạn chế khác nhau về độ rộng của trường bit
  • Máy tính khác nhau về cách lưu trữ các byte trong một từ (Intel 80x86 và Motorola 68000)

Cách căn chỉnh hoạt động:

  • Khối lượng chiếm bởi cấu trúc được tính bằng kích thước của phần tử đơn được căn chỉnh của một mảng các cấu trúc đó. Cấu trúc phải kết thúc sao cho phần tử đầu tiên của cấu trúc tiếp theo không vi phạm các yêu cầu căn chỉnh

ps Thông tin chi tiết có sẵn tại đây: "Samuel P.Harbison, Guy L.Steele CA Reference, (5.6.2 - 5.6.7)"


2

Ý tưởng là để xem xét tốc độ và bộ đệm, toán hạng nên được đọc từ các địa chỉ được căn chỉnh theo kích thước tự nhiên của chúng. Để thực hiện điều này, các trình biên dịch cấu trúc thành viên để các thành viên sau hoặc cấu trúc sau sẽ được căn chỉnh.

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

Kiến trúc x86 luôn có thể tìm nạp các địa chỉ sai. Tuy nhiên, nó chậm hơn và khi căn chỉnh sai chồng lấp hai dòng bộ đệm khác nhau, thì nó sẽ hiển thị hai dòng bộ đệm khi một truy cập được căn chỉnh sẽ chỉ loại bỏ một dòng.

Một số kiến ​​trúc thực sự phải mắc kẹt trong việc đọc và ghi sai, và các phiên bản đầu tiên của kiến ​​trúc ARM (phiên bản phát triển thành tất cả các CPU di động ngày nay) ... tốt, chúng thực sự chỉ trả về dữ liệu xấu cho những thứ đó. (Họ bỏ qua các bit thứ tự thấp.)

Cuối cùng, lưu ý rằng các dòng bộ đệm có thể lớn tùy ý và trình biên dịch không cố đoán chúng hoặc tạo ra sự đánh đổi giữa không gian và tốc độ. Thay vào đó, các quyết định căn chỉnh là một phần của ABI và thể hiện sự liên kết tối thiểu cuối cùng sẽ lấp đầy một dòng bộ đệm.

TL; DR: căn chỉnh là quan trọng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.