Cấu trúc bố cục bộ nhớ trong C


85

Tôi có nền tảng C #. Tôi là một người mới học ngôn ngữ cấp thấp như C.

Trong C #, structbộ nhớ của được đặt bởi trình biên dịch theo mặc định. Trình biên dịch có thể sắp xếp lại thứ tự các trường dữ liệu hoặc đệm các bit bổ sung giữa các trường một cách ngầm định. Vì vậy, tôi đã phải chỉ định một số thuộc tính đặc biệt để ghi đè hành vi này cho bố cục chính xác.

AFAIK, C không sắp xếp lại hoặc căn chỉnh bố cục bộ nhớ của a structtheo mặc định. Tuy nhiên, tôi nghe nói có một ngoại lệ nhỏ rất khó tìm.

Hành vi bố trí bộ nhớ của C là gì? Điều gì nên được sắp xếp lại / căn chỉnh và không?

Câu trả lời:


110

Trong C, trình biên dịch được phép chỉ định một số căn chỉnh cho mọi kiểu nguyên thủy. Thông thường, căn chỉnh là kích thước của kiểu. Nhưng nó hoàn toàn cụ thể về triển khai.

Các byte đệm được đưa vào để mọi đối tượng được căn chỉnh đúng cách. Sắp xếp lại không được phép.

Có thể mọi trình biên dịch hiện đại từ xa đều triển khai #pragma packcho phép kiểm soát phần đệm và để lập trình viên tuân thủ ABI. (Tuy nhiên, nó hoàn toàn không đạt tiêu chuẩn.)

Từ C99 §6.7.2.1:

12 Mỗi thành viên không phải là trường bit của một cấu trúc hoặc đối tượng liên hợp được căn chỉnh theo cách thức triển khai được xác định phù hợp với kiểu của nó.

13 Trong một đối tượng cấu trúc, các thành viên không phải trường bit và các đơn vị chứa trường bit có địa chỉ tăng theo thứ tự khai báo chúng. Một con trỏ đến một đối tượng cấu trúc, được chuyển đổi phù hợp, 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. 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 ở phần đầu của nó.


1
Một số trình biên dịch (tức là GCC) thực hiện hiệu ứng tương tự #pragma packnhưng với sự kiểm soát chi tiết hơn đối với ngữ nghĩa.
Chris Lutz

21
Tôi ngạc nhiên khi thấy một phiếu giảm giá. Bất cứ ai có thể chỉ ra lỗi?
Potatoswatter

2
C11 cũng có _Alignas.
idmean

117

Nó dành riêng cho việc triển khai, nhưng trên thực tế, quy tắc (trong trường hợp không có #pragma packhoặc tương tự) là:

  • Các thành viên cấu trúc được lưu trữ theo thứ tự mà chúng được khai báo. (Đây là yêu cầu của tiêu chuẩn C99, như đã đề cập ở đây trước đó.)
  • Nếu cần, đệm được thêm vào trước mỗi thành viên cấu trúc để đảm bảo căn chỉnh chính xác.
  • Mỗi kiểu nguyên thủy T yêu cầu một sự liên kết của các sizeof(T)byte.

Vì vậy, với cấu trúc sau:

struct ST
{
   char ch1;
   short s;
   char ch2;
   long long ll;
   int i;
};
  • ch1 đang ở độ lệch 0
  • một byte đệm được chèn để căn chỉnh ...
  • s ở phần bù 2
  • ch2 ở mức bù 4, ngay sau s
  • 3 byte đệm được chèn để căn chỉnh ...
  • ll ở bù 8
  • i ở mức bù 16, ngay sau ll
  • 4 byte đệm được thêm vào cuối để cấu trúc tổng thể là bội số của 8 byte. Tôi đã kiểm tra điều này trên hệ thống 64 bit: Hệ thống 32 bit có thể cho phép các cấu trúc có sự liên kết 4 byte.

sizeof(ST)24 cũng vậy .

Nó có thể được giảm xuống 16 byte bằng cách sắp xếp lại các thành viên để tránh đệm:

struct ST
{
   long long ll; // @ 0
   int i;        // @ 8
   short s;      // @ 12
   char ch1;     // @ 14
   char ch2;     // @ 15
} ST;

3
Nếu cần thiết, phần đệm được thêm vào trước ... Giống như sau. Tốt nhất hãy thêm một charthành viên cuối cùng vào ví dụ của bạn.
Deduplicator

9
Một kiểu nguyên thủy không nhất thiết yêu cầu sự liên kết của các sizeof(T)byte. Ví dụ, một doublekiến trúc 32 bit phổ biến là 8 byte nhưng thường chỉ yêu cầu căn chỉnh 4 byte . Hơn nữa, phần đệm ở cuối cấu trúc chỉ đệm cho sự liên kết của thành viên cấu trúc rộng nhất. Ví dụ, một cấu trúc gồm 3 biến char không thể có phần đệm.
Matt,

1
@ dan04, có nên bố trí cấu trúc theo thứ tự giảm dần của sizeof (T) không. Có bất kỳ nhược điểm nào khi làm điều này không?
RohitMat

11

Bạn có thể bắt đầu bằng cách đọc bài viết về căn chỉnh cấu trúc dữ liệu trên wikipedia để hiểu rõ hơn về căn chỉnh dữ liệu.

Từ bài báo wikipedia :

Căn chỉnh dữ liệu có nghĩa là đặt dữ liệu ở độ lệch bộ nhớ bằng bội số của kích thước từ, điều này làm tăng hiệu suất của hệ thống do cách CPU xử lý bộ nhớ. Để căn chỉnh dữ liệu, có thể cần phải chèn một số byte vô nghĩa vào giữa phần cuối của cấu trúc dữ liệu cuối cùng và phần bắt đầu của phần tiếp theo, đó là phần đệm cấu trúc dữ liệu.

Từ 6.54.8 Cấu trúc-Đóng gói Pragmas của tài liệu GCC:

Để tương thích với trình biên dịch Microsoft Windows, GCC hỗ trợ một tập hợp các chỉ thị #pragma thay đổi sự liên kết tối đa của các thành viên của cấu trúc (không phải là trường bit-width), liên hiệp và các lớp được xác định sau đó. Giá trị n bên dưới luôn được yêu cầu là lũy thừa nhỏ của hai và chỉ định căn chỉnh mới theo byte.

  1. #pragma pack(n) chỉ cần đặt căn chỉnh mới.
  2. #pragma pack() đặt căn chỉnh thành căn chỉnh có hiệu lực khi bắt đầu biên dịch (xem thêm tùy chọn dòng lệnh -fpack-struct [=] xem Tùy chọn mã Gen).
  3. #pragma pack(push[,n]) đẩy cài đặt căn chỉnh hiện tại lên một ngăn xếp bên trong và sau đó tùy chọn đặt căn chỉnh mới.
  4. #pragma pack(pop)khôi phục cài đặt căn chỉnh về cài đặt được lưu ở đầu ngăn xếp bên trong (và xóa mục nhập ngăn xếp đó). Lưu ý rằng điều #pragma pack([n])đó không ảnh hưởng đến ngăn xếp nội bộ này; do đó, có thể được #pragma pack(push) theo sau bởi nhiều #pragma pack(n) trường hợp và được hoàn thiện bởi một trường hợp duy nhất #pragma pack(pop).

Một số mục tiêu, ví dụ: i386 và powerpc, hỗ trợ ms_struct #pragmađể đưa ra một cấu trúc dưới dạng tài liệu __attribute__ ((ms_struct)).

  1. #pragma ms_struct on bật bố cục cho các cấu trúc được khai báo.
  2. #pragma ms_struct off tắt bố cục cho các cấu trúc đã khai báo.
  3. #pragma ms_struct reset quay trở lại bố cục mặc định.

Cảm ơn đã quan tâm. Tôi đã sửa đổi câu hỏi như bạn đã hướng dẫn.
eonil
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.