2013-04-12 44 views
7

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) intint 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ố _Boolint.

Đố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) 
+0

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). –

+0

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. –

+3

@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

Trả lời

5

Trong một cách bạn đã trả lời câu hỏi cho mình với trích dẫn này từ tiêu chuẩn:

sự liên kết của các đơn vị lưu trữ địa chỉ không được chỉ định.

Trình biên dịch có thể chọn bất kỳ căn chỉnh nào và tuân theo tiêu chuẩn C, nhưng đó không phải là toàn bộ câu chuyện.

Để mã được biên dịch với các trình biên dịch khác nhau để tương thích, nền tảng ABI phải chỉ định các chi tiết này. Ví dụ như SYS-V i386 ABI sử dụng bởi Linux x86 nói:

Bit-lĩnh vực tuân theo kích thước và liên kết quy tắc tương tự như cấu trúc và công đoàn các thành viên khác, với bổ sung sau đây: [...]

  • Một trường bit phải nằm hoàn toàn trong đơn vị lưu trữ phù hợp với loại được khai báo .

Nó sau đó sau đó không phụ thuộc vào chiều rộng, một bitfield long phải cư trú tại cái gì đó được xếp trên một ranh giới 4 byte.

+0

Vâng, tôi sẽ không nói rằng tôi đã tự trả lời câu hỏi. Trong mọi trường hợp, điều đó đặt ra một câu hỏi khác: lý do cơ bản cho yêu cầu đó trong ABI là gì? Tôi cho rằng nó phải thực thi * một số quy tắc *, nhưng tại sao lại là vậy? (Tôi gần như có thể giải thích về "loại khai báo" của nó có nghĩa là gì, tiêu chuẩn C nói "Một trường bit được hiểu là có một số nguyên đã ký hoặc không dấu bao gồm số bit được chỉ định ", nhưng ngữ cảnh trong [ Tài liệu ABI] (http://www.sco.com/developers/devspecs/abi386-4.pdf) ngụ ý rằng "loại" của 'int bf: 1' là' int'.) –

Các vấn đề liên quan