Thứ nhất, đây là những gì tiêu chuẩn ISO C nói về các lĩnh vực bit, trích dẫn dự thảo N1570 của 2011 ISO C Standard, phần 6.7.2.1:Tại sao loại trường bit ảnh hưởng đến kích thước của cấu trúc chứa?
A-trường bit sẽ có một loại đó là một trình độ hoặc không đủ tiêu chuẩn phiên bản
_Bool
,signed int
,unsigned int
hoặc một số khác loại thực hiện được xác định. Nó được thực hiện xác định cho dù loại nguyên tử được phép.
...
A-trường bit được hiểu như là có một loại nguyên ký kết hoặc unsigned gồm số quy định của bit. Nếu giá trị 0 hoặc 1 là được lưu trữ trong trường bit có chiều rộng không đồng bộ loại
_Bool
, giá trị của trường bit sẽ được so sánh bằng giá trị được lưu trữ; a_Bool
trường bit có ngữ nghĩa của một_Bool
.Việc triển khai có thể phân bổ bất kỳ đơn vị lưu trữ địa chỉ lớn nào đủ để giữ trường bit. Nếu đủ không gian còn lại, một trường bit là ngay sau một trường bit khác trong một cấu trúc sẽ được đóng gói thành các bit liền kề của cùng một đơn vị. Nếu không đủ không gian, cho dù một trường bit không phù hợp được đưa vào đơn vị tiếp theo hoặc chồng chéo các đơn vị lân cận được xác định thực hiện. Thứ tự của phân bổ các trường bit trong một đơn vị (thứ tự cao đến thứ tự thấp hoặc thứ tự thấp đến thứ tự cao) được thực hiện xác định. Căn chỉnh đơn vị lưu trữ địa chỉ không được chỉ định.
Đối với bất kỳ loại nào struct
, căn chỉnh loại ít nhất là căn chỉnh tối đa của bất kỳ thành viên nào và kích thước của bất kỳ loại nào là bội số của căn chỉnh. Ví dụ: nếu cấu trúc chứa thành viên (không phải bit-bit) int
và int
yêu cầu căn chỉnh 4 byte thì bản thân cấu trúc yêu cầu căn chỉnh 4 byte trở lên.
Nhiều trình biên dịch cho phép các trường bit của các loại số nguyên khác với các loại số _Bool
và int
.
Đối với ít nhất một số trình biên dịch, sự liên kết của một struct
chứa một trường bit ít nhất là sự liên kết của kiểu tuyên bố của trường bit. Ví dụ, đối 4.7.2 gcc trên x86_64, đưa ra:
struct sb {
_Bool bf:1;
};
struct si {
unsigned bf:1;
};
gcc cho struct sb
một kích thước và sự liên kết của 1 byte (đó là kích thước và sự liên kết của _Bool
), và struct si
một kích thước và sự liên kết của 4 byte (đó là kích thước và sự liên kết của int
). Nó thực hiện tương tự với các trường bit của các kiểu được định nghĩa thực hiện; một trường bit được định nghĩa là long long bf:1;
buộc một kích thước 8 byte và căn chỉnh cho cấu trúc bao quanh. Điều này được thực hiện mặc dù trong cả hai trường hợp, trường bit bf
là một đối tượng có chiều rộng chỉ là 1 bit.
Tôi đã nhìn thấy hành vi tương tự với trình biên dịch của Sun trên SPARC/Solaris 9.
Thử nghiệm cho thấy nhiều trường bit được xác định là _Bool
hoặc là unsigned
có thể được đóng gói thành các bit liền kề trong một byte đơn (thực tế là bắt buộc), vì vậy các trường bit không có yêu cầu liên kết chặt chẽ.
Tôi hiểu rằng bố cục của các thành viên cấu trúc phần lớn được thực hiện xác định và tôi không tin rằng hành vi của gcc vi phạm tiêu chuẩn C.
Vì vậy, câu hỏi của tôi (cuối cùng!) là, tại sao gcc (cùng với ít nhất một trình biên dịch C không liên quan và có thể nhiều hơn) làm điều này? Do các tác giả của gcc giả định rằng loại khai báo của một trường bit phải ảnh hưởng đến kích thước và căn chỉnh của cấu trúc chứa? Chúng có đúng trong giả định này không? Có một yêu cầu trong tiêu chuẩn C mà tôi đã bỏ lỡ?
Đây là chương trình thử nghiệm thể hiện hành vi. Nếu bạn muốn chạy nó trên hệ thống của bạn, bạn có thể cần phải bình luận ra các phần của nó, nếu bạn đang sử dụng một trình biên dịch cũ mà không hỗ trợ một số tính năng mới hơn, hoặc một trong đó không cho phép một số loại bit lĩnh vực. Tôi muốn được biết nếu có các trình biên dịch mà không hoạt động như gcc.
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
int main(void) {
struct sb { _Bool bf:1; };
struct s8 { uint8_t bf:1; };
struct s16 { uint16_t bf:1; };
struct s32 { uint32_t bf:1; };
struct s64 { uint64_t bf:1; };
printf("sizeof (struct sb) = %2zu (%2zu bits)\n",
sizeof (struct sb),
sizeof (struct sb) * CHAR_BIT);
printf("sizeof (struct s8) = %2zu (%2zu bits)\n",
sizeof (struct s8),
sizeof (struct s8) * CHAR_BIT);
printf("sizeof (struct s16) = %2zu (%2zu bits)\n",
sizeof (struct s16),
sizeof (struct s16) * CHAR_BIT);
printf("sizeof (struct s32) = %2zu (%2zu bits)\n",
sizeof (struct s32),
sizeof (struct s32) * CHAR_BIT);
printf("sizeof (struct s64) = %2zu (%2zu bits)\n",
sizeof (struct s64),
sizeof (struct s64) * CHAR_BIT);
return 0;
}
Dưới đây là kết quả tôi nhận được trên hệ thống của tôi:
sizeof (struct sb) = 1 (8 bits)
sizeof (struct s8) = 1 (8 bits)
sizeof (struct s16) = 2 (16 bits)
sizeof (struct s32) = 4 (32 bits)
sizeof (struct s64) = 8 (64 bits)
Lấy cảm hứng từ cuộc thảo luận trong nhận xét về [câu hỏi này] (http://stackoverflow.com/q/15977101/827263). –
clang (ít nhất là cái tôi đã cài đặt trên hộp 32 bit tôi hiện đang bật) nói 32 bit cho 'struct s64', và vì vậy hãy nói gcc của tôi ở đây. –
@DanielFischer clang 3.0, icc 13.1.1, tcc 0.9.25 và gcc 4.7 tạo ra kết quả chính xác được đăng bởi Keith. (trên một hộp amd64) – cnicutar