2008-09-29 43 views
7

Tôi có một ++ chương trình C đại diện cho một tiêu đề TCP như một cấu trúc:Bắt kích thước tiêu đề khác nhau bằng cách thay đổi kích thước cửa sổ

#include "stdafx.h" 

/* TCP HEADER 

    0     1     2     3 
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |   Source Port   |  Destination Port  | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |      Sequence Number      | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |     Acknowledgment Number      | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    | Data |   |U|A|P|R|S|F|        | 
    | Offset| Reserved |R|C|S|S|Y|I|   Window    | 
    |  |   |G|K|H|T|N|N|        | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |   Checksum   |   Urgent Pointer  | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |     Options     | Padding | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
    |        data        | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

*/ 

typedef struct {  // RFC793 
    WORD   wSourcePort; 
    WORD   wDestPort; 
    DWORD  dwSequence; 
    DWORD  dwAcknowledgment; 
    unsigned int byReserved1:4; 
    unsigned int byDataOffset:4; 
    unsigned int fFIN:1; 
    unsigned int fSYN:1; 
    unsigned int fRST:1; 
    unsigned int fPSH:1; 
    unsigned int fACK:1; 
    unsigned int fURG:1; 
    unsigned int byReserved2:2; 
    unsigned short wWindow; 
    WORD   wChecksum; 
    WORD   wUrgentPointer; 
} TCP_HEADER, *PTCP_HEADER; 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    printf("TCP header length: %d\n", sizeof(TCP_HEADER)); 
    return 0; 
} 

Nếu tôi chạy chương trình này tôi nhận được kích thước của tiêu đề này là 24 byte, mà không phải là kích thước tôi mong đợi. Nếu tôi thay đổi kiểu của trường "wWindow" thành "unsigned int wWindow: 16", có cùng số lượng bit như một unsigned short, chương trình cho tôi biết kích thước của struct bây giờ là 20 byte, đúng kích cỡ. Tại sao điều này?

Tôi đang sử dụng Microsoft Visual Studio 2005 với SP1 trên máy 32 bit x86.

Trả lời

2

Xem câu hỏi này: Why isn't sizeof for a struct equal to the sum of sizeof of each member?.

Tôi tin rằng trình biên dịch có một gợi ý để vô hiệu hóa đệm khi bạn sử dụng cú pháp "unsigned int wWindow: 16".

Ngoài ra, lưu ý rằng đoạn mã ngắn không được đảm bảo là 16 bit. Sự bảo đảm là: 16 bit < = kích thước của một < ngắn = kích thước của một int.

+0

@andy: +1, có thể bao gồm #pragma push/pop trên tham số gói để giúp anh ấy. – user7116

+0

Mike B có câu trả lời đúng dưới đây. Xem thêm phần thảo luận này: http://groups.google.com/group/microsoft.public.dotnet.languages.vc/browse_frm/thread/7ea120d16c49611d/bdf918a490a6d61a?lnk=st&q=bitfield#bdf918a490a6d61a – ChrisN

6

Bởi vì trình biên dịch đóng gói bitfield của bạn vào một int 32 bit, không phải là thực thể 16 bit.

Nói chung, bạn nên tránh bitfields và sử dụng các hằng số biểu hiện khác (enums hoặc bất kỳ) với mặt nạ bit rõ ràng và dịch chuyển để truy cập 'các trường con' trong một trường.

Đây là một trong những lý do tại sao bitfields nên tránh - chúng không phải là rất di động giữa các trình biên dịch ngay cả đối với cùng một nền tảng. từ các tiêu chuẩn C99 (có từ ngữ tương tự trong tiêu chuẩn C90):

An thực hiện có thể phân bổ bất kỳ đơn vị lưu trữ địa chỉ đủ lớn tổ chức một bitfield. Nếu đủ không gian vẫn còn, một trường bit ngay lập tức theo một trường bit khác trong 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 thiếu không gian còn trống, cho dù trường bit không khớp là đặt vào đơn vị kế tiếp hoặc chồng chéo đơn vị liền kề là đượ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) là được xác định thực hiện. Căn chỉnh của đơn vị lưu trữ địa chỉ là không xác định.

Bạn không thể đảm bảo liệu một trường bit sẽ 'mở' ranh giới int hay không và bạn không thể chỉ định liệu một bitfield có bắt đầu ở đầu thấp hay không. độc lập cho dù bộ vi xử lý là lớn-endian hay little-endian).

+0

Tôi tự hỏi liệu có ai sẽ định nghĩa phương tiện di động chỉ định bitfield, ít nhất là trên phần cứng tương thích với POSIX hay không, ví dụ: sử dụng cú pháp của một cái gì đó như "uInt32_t thing1, thing2; field1: thing1.28.4; field2: thing1.0.28; field3: thing2.12.20;" v.v. Các khai báo trường như vậy sẽ không phân bổ không gian, nhưng sẽ truy cập các trường được phân bổ trước đó. Tôi đã nhìn thấy trình biên dịch với danh pháp cho lớp phủ bit cờ trên các biến được khai báo tĩnh; Tôi tự hỏi, nếu có một cú pháp cho lớp phủ bit trên các lĩnh vực cấu trúc khác? – supercat

0

Trình biên dịch sẽ thêm thành phần cấu trúc non-bitfield vào 32-bit - căn chỉnh từ gốc. Để sửa lỗi này, hãy #pragma pack (0) trước cấu trúc và #pragma pack() sau.

+0

#pragma pack (0) không thay đổi hành vi. –

0

Ranh giới cấu trúc trong bộ nhớ có thể được đệm bởi trình biên dịch tùy thuộc vào kích thước và thứ tự của các trường.

0

Không phải chuyên gia C/C++ khi nói đến đóng gói. Nhưng tôi tưởng tượng có một quy tắc trong spec nói rằng khi một bitfield không theo một bitfield nó phải được căn chỉnh trên ranh giới từ bất kể nó có vừa với không gian còn lại hay không. Bằng cách làm cho nó một bitvector rõ ràng bạn đang tránh vấn đề này.

Một lần nữa đây là suy đoán với một liên lạc kinh nghiệm.

0

Thú vị - Tôi nghĩ rằng "WORD" sẽ đánh giá là "không ký tên ngắn", vì vậy bạn có vấn đề đó ở nhiều nơi.

Cũng lưu ý rằng bạn sẽ cần xử lý các vấn đề về cuối cùng với bất kỳ giá trị nào vượt quá 8 bit.

0

Bạn đang thấy các giá trị khác nhau vì quy tắc đóng gói trình biên dịch. Bạn có thể xem các quy tắc cụ thể cho studio trực quan here.

Khi bạn có cấu trúc phải được đóng gói (hoặc tuân theo một số yêu cầu căn chỉnh cụ thể), bạn nên sử dụng tùy chọn #pragma pack(). Đối với mã của bạn, bạn có thể sử dụng gói #pragma (0) sẽ căn chỉnh tất cả các thành viên cấu trúc trên các ranh giới byte. Sau đó bạn có thể sử dụng gói #pragma() để đặt lại cấu trúc đóng gói về trạng thái mặc định của nó. Bạn có thể xem thêm thông tin về gói pragma here.

4

Loạt bit "bit int int: xx" của bạn chỉ sử dụng 16 trong số 32 bit trong một int. 16 bit khác (2 byte) có, nhưng không sử dụng. Tiếp theo là dấu ngắn chưa ký, nằm trên một đường biên int, và sau đó là một WORD, được căn chỉnh dọc theo một đường biên int có nghĩa là có 2 byte đệm giữa chúng.

Khi bạn chuyển sang "unsigned int wWindow: 16", thay vì là một đoạn ngắn riêng biệt, trình biên dịch sử dụng các phần không sử dụng của bitfield trước đó, vì vậy không lãng phí, không ngắn và không có phần đệm sau ngắn lưu bốn byte.

0

Tôi nghĩ Mike B đã làm đúng, nhưng không hoàn toàn rõ ràng. Khi bạn yêu cầu "ngắn", nó được căn chỉnh trên ranh giới 32 bit. Khi bạn yêu cầu int: 16, nó không phải. Vì vậy, int: 16 phù hợp ngay sau khi các trường ebit thứ, trong khi ngắn bỏ qua 2 byte và bắt đầu tại khối 32 bit tiếp theo.

Phần còn lại của những gì anh ấy nói là hoàn toàn có thể áp dụng được - trường bit không bao giờ được sử dụng để mã hóa cấu trúc bên ngoài, vì không có sự đảm bảo về cách phân bổ chúng. Tốt nhất, chúng thuộc về các chương trình nhúng nơi lưu một byte là quan trọng. Và thậm chí ở đó, bạn không thể sử dụng chúng để thực sự kiểm soát các bit trong các cổng được ánh xạ trên bộ nhớ.

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