2015-04-01 24 views
8

Xem ví dụ trực tuyến: Ideone exampleTại sao kích thước của cấu trúc đóng gói 5 thay vì 4 byte ở đây?

struct { 
    union { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Tại sao các báo cáo trình biên dịch kích thước của struct là 5 byte thay vì 4 ở đây? Nó phải chứa 32 bit.

+0

có thể trùng lặp của [Cấu trúc và Liên minh trong C, xác định kích thước và truy cập thành viên] (http://stackoverflow.com/questions/3380118/structures-and-unions-in-c -determining-size-and-access-members) –

+0

@DavidTitarenco: Tôi không nghĩ rằng bài đăng cụ thể đó tốt cho bitfields. Nó sẽ không làm tôi ngạc nhiên nếu điều này đã được trả lời ở nơi khác tho '. –

+4

Người trả lời cần chú ý rằng OP đang sử dụng [không chuẩn __attribute__ ((đóng gói)) '] (https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html) GCC mở rộng để tránh đệm . Vì vậy, nói rằng trình biên dịch được phép chèn đệm là không chính xác - khi '__attribute __ ((đóng gói))' được sử dụng, GCC sẽ tổ chức cấu trúc để tránh đệm (làm cho truy cập đắt hơn trên nền tảng không hỗ trợ đọc không đồng nhất). – user4815162342

Trả lời

5

Đó là do căn chỉnh bộ nhớ: Trình biên dịch sẽ không bắt đầu canFlags ở giữa giữa của một byte, nó sẽ bắt đầu nó ở đầu byte tiếp theo (có thể *). Vì vậy, bạn có bốn byte cho liên minh ban đầu của bạn và một byte cho canFlags.

Nếu, ví dụ, bạn di chuyển canFlags vào công đoàn, nó sẽ (có thể là *) có kích thước 4:

typedef struct structTag { 
    union { 
    struct { 
     uint32_t messageID : 26; /* 26bit message id, 67108864 ids */ 
     uint8_t priority : 3; /* priority: MUST BE 0 */ 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    uint8_t canFlags : 3; /* <==== Moved */ 
    } __attribute__ ((packed)); 
} __attribute__ ((packed)) idSpecial; 

Updated example on ideone. Rõ ràng, thay đổi cụ thể đó có thể không phải là thứ bạn muốn; Tôi chỉ chứng minh rằng vấn đề đang bắt đầu một lĩnh vực mới không phải trên một ranh giới byte.


* "có thể là" vì cuối cùng nó phụ thuộc vào trình biên dịch.

+0

canFlags bên cạnh rawid làm cho họ công đoàn - đó không phải là những gì OP muốn, tôi nghĩ. –

+0

@MatsPetersson: Phải, OP sẽ phải thiết kế lại, tôi đã chỉ ra lý do tại sao họ nhận được 5 thay vì 4 cho thiết kế hiện tại của họ. –

+1

Sử dụng gợi ý của bạn, chúng tôi đã bao bọc 'rawID' và' canFlags' trong cấu trúc và thêm phần đệm giả 3 bit ở cuối cấu trúc đầu tiên bên trong liên kết và bây giờ toàn bộ cấu trúc là 4 byte lớn như mong đợi. –

0

Trong một số trình biên dịch, để "hợp nhất" các bit, tất cả các mục phải cùng loại. Vì vậy, hãy đặt nó uint32_t nơi bạn hiện có uint8_t - điều này dường như không phải là trường hợp trong trình biên dịch IdeOne sử dụng tho '

[Không có vấn đề gì, Nó vẫn còn tùy thuộc vào trình biên dịch cách để đảm bảo rằng dữ liệu của bạn được lưu trữ dưới dạng 32 bit là sử dụng một đơn uint32_t và khai báo lớp có chuyển dịch và/hoặc điều chỉnh giá trị - sự đảm bảo duy nhất bạn có là phần tử ONE trong cấu trúc của bạn sẽ có ít nhất là nhiều bit như bạn đã yêu cầu]

Như những người khác đã chỉ ra, bạn không thể bắt đầu một cấu trúc mới ngoài ranh giới byte. Tôi cố định nó bằng cách có một cấu trúc thứ hai bên trong công đoàn, như thế này: http://ideone.com/Mr1gjD

#include <stdint.h> 
#include <stdio.h> 

typedef struct structTag { 
    union { 
    struct { 
     uint32_t messageID : 26; /* 26bit message id, 67108864 ids */ 
     uint8_t priority : 3; /* priority: MUST BE 0 */ 
    } __attribute__ ((packed)); 
    struct { 
     uint32_t rawID : 29; 
     uint8_t canFlags : 3; 
    }; 
    } __attribute__ ((packed)); 
} __attribute__ ((packed)) idSpecial; 

int main() { 
    printf("size: %d", sizeof(idSpecial)); 
    return 0; 
} 
+0

@ T.J.Crowder: Đồng ý. Mặc dù tôi đã thấy những trường hợp mà vấn đề này vì một số lý do - tôi nghĩ rằng nó có thể là trình biên dịch MS có vấn đề đó. –

6

Vấn đề là __attribute__((packed)) không thực hiện đóng gói trên bit. Nó chỉ đảm bảo rằng không có đệm giữa struct thành viên. Bạn có thể thử ví dụ đơn giản này, trong đó kích thước cũng được báo cáo là 5:

typedef struct structTag { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Chỉ đóng gói bitwise cho các thành viên bitfield. Bạn sẽ cần phải thiết kế lại cấu trúc của bạn để trở thành một liên minh của một cấu trúc với bitfields messageID/priority/canFlags và một cấu trúc với bitfields rowID/canFlags. Nói cách khác, bạn sẽ cần phải có một số bản sao hoặc khu nghỉ mát để truy cập macro hoặc chức năng thành viên.

1

Dữ liệu được sắp xếp và truy cập trong bộ nhớ máy tính bằng cách sử dụng Căn chỉnh dữ liệu. Trong đó có hai vấn đề liên quan

  1. Alignment
  2. Padding

Khi một hoạt động viết được thực hiện bằng máy tính, Nó thường viết trong bội số của 4 byte (cho các hệ thống 32 bit). Một lý do cho hành động này là mục tiêu tăng hiệu suất.Vì vậy, khi bạn đang viết bất kỳ cấu trúc dữ liệu nào, có biến số 1 byte đầu tiên và sau đó dữ liệu biến 4 byte, nó sẽ làm đệm sau dữ liệu byte 1 đầu tiên để căn chỉnh nó trên ranh giới 32 bit.

struct { 
    union { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Bây giờ trong cấu trúc dữ liệu ở trên bạn đang sử dụng __attribute__ ((packed)) có nghĩa là không có đệm. Vì vậy, uint32_t là 4 byte, nhưng bạn đang saing nó có 26 bit và 3 bit cho ưu tiên. Bây giờ khi bạn có cả hai biến trong một cấu trúc để nó sẽ dự trữ 32 bit thay vì 29 để thông tin cấu trúc đầu tiên của bạn được gán cho các ranh giới.

Bây giờ cho canFlags Nó sẽ cần một byte khác. Vì vậy, làm cho 5 byte thay vì 4 byte thay vì 4.

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