Thận trọng của bạn về "tại sao không sử dụng một mảng có kích thước 1" được phát hiện.
Mã đang thực hiện "C struct hack" sai, vì khai báo các mảng có độ dài bằng không là một vi phạm ràng buộc. Điều này có nghĩa là một trình biên dịch có thể từ chối quyền tấn công của bạn ngay lập tức trên con dơi tại thời gian biên dịch với một thông báo chẩn đoán ngừng dịch.
Nếu chúng tôi muốn tiến hành hack, chúng tôi phải lén qua trình biên dịch.
Cách đúng để làm "C struct hack" (đó là tương thích với C tiếng địa phương sẽ trở lại đến 1989 ANSI C, và có lẽ sớm hơn nhiều) là sử dụng một mảng hoàn toàn hợp lệ của kích thước 1:
struct someData
{
int nData;
unsigned char byData[1];
}
Hơn nữa, thay vì sizeof struct someData
, kích thước của phần trước byData
được tính bằng:
offsetof(struct someData, byData);
để phân bổ một struct someData
với không gian cho 42 byte trong byData
, sau đó chúng ta sẽ sử dụng:
struct someData *psd = (struct someData *) malloc(offsetof(struct someData, byData) + 42);
Lưu ý rằng phép tính offsetof
này là sự tính toán chính xác ngay cả trong trường hợp kích thước mảng bằng 0. Bạn thấy, sizeof
toàn bộ cấu trúc có thể bao gồm đệm. Ví dụ, nếu chúng ta có một cái gì đó như thế này:
struct hack {
unsigned long ul;
char c;
char foo[0]; /* assuming our compiler accepts this nonsense */
};
Kích thước của struct hack
là hoàn toàn có thể đệm cho sự liên kết vì sự ul
thành viên. Nếu unsigned long
rộng bốn byte, thì có thể là sizeof (struct hack)
là 8, trong khi offsetof(struct hack, foo)
gần như chắc chắn 5. Phương pháp offsetof
là cách để có được kích thước chính xác của phần trước của cấu trúc ngay trước mảng.
Vì vậy, đó sẽ là cách để cấu trúc lại mã: làm cho nó phù hợp với cấu trúc hack cổ điển, dễ di động.
Tại sao không sử dụng con trỏ? Bởi vì một con trỏ chiếm thêm không gian và phải được khởi tạo.
Có nhiều lý do chính đáng khác không sử dụng con trỏ, cụ thể là con trỏ yêu cầu không gian địa chỉ để có ý nghĩa. Cấu trúc hack là ngoại lệ: có nghĩa là, có những tình huống trong đó bố trí như vậy phù hợp với lưu trữ bên ngoài như các vùng của tệp, gói hoặc bộ nhớ dùng chung, trong đó bạn không muốn con trỏ vì chúng không có ý nghĩa.
Cách đây vài năm, tôi đã sử dụng cấu trúc hack trong thông báo bộ nhớ chia sẻ truyền giao diện giữa nhân và không gian người dùng. Tôi không muốn có con trỏ ở đó, bởi vì chúng sẽ chỉ có ý nghĩa với không gian địa chỉ gốc của tiến trình tạo ra một thông điệp. Phần hạt nhân của phần mềm có một cái nhìn vào bộ nhớ bằng cách sử dụng ánh xạ của chính nó ở một địa chỉ khác, và vì vậy mọi thứ đều dựa trên các phép tính bù trừ.
Đây là "cấu trúc hack", được mô tả trong câu hỏi 2.6 của [comp.lang.c FAQ] (http://www.c-faq.com/). Dennis Ritchie gọi nó là "chumminess không được bảo đảm với việc thực hiện C". C99 giới thiệu một tính năng ngôn ngữ mới, "linh hoạt mảng thành viên", để thay thế cấu trúc hack. Ngay cả trình biên dịch của Microsoft, được chú ý vì thiếu sự hỗ trợ C99, hỗ trợ các thành viên mảng linh hoạt. –
KHÔNG thêm thẻ 'c' vào câu hỏi này.Các quy tắc C++ cho điều này khá khác với các quy tắc C. –
@BenVoigt Câu trả lời được chấp nhận là mã C thuần túy, vì vậy tôi đoán chỉnh sửa của bạn sai. c hack áp dụng cho cả c và C++ theo cùng một cách –