2012-04-21 68 views
6

Visual C++ cung cấp cả công tắc trình biên dịch (/Zp) và pragma pack để ảnh hưởng đến cấu trúc của các cấu trúc. Tuy nhiên, tôi dường như có một số quan niệm sai lầm về cách họ làm việc.Căn chỉnh cấu trúc trong Visual C++

Theo MSDN ứng với giá trị n liên kết nhất định,

sự liên kết của thành viên sẽ được trên một ranh giới đó là một trong hai bội của n hoặc bội số của kích thước của các thành viên, tùy theo là nhỏ hơn.

Giả sử giá trị gói là 8 byte (mặc định). Trong một cấu trúc, tôi nghĩ rằng bất kỳ thành viên nào có kích thước nhỏ hơn 8 byte sẽ có mức chênh lệch là bội số của kích thước của nó. Bất kỳ thành viên nào có kích thước từ 8 byte trở lên sẽ có mức chênh lệch là bội số của 8 byte.

Bây giờ uống theo chương trình sau:

#include <tchar.h> 

#pragma pack(8) 

struct Foo { 
    int i1; 
    int i2; 
    char c; 
}; 

struct Bar { 
    char c; 
    Foo foo; 
}; 

int _tmain(int argc, _TCHAR* argv[]) { 
    int fooSize = sizeof(Foo); // yields 12 
    Bar bar; 
    int fooOffset = ((int) &bar.foo) - ((int) &bar); // yields 4 

    return 0; 
} 

Cấu trúc Foo là 12 byte trong kích thước. Vì vậy, trong phạm vi Bar, tôi mong đợi các thành viên Foo được bù đắp 8 (bội số của 8) trong khi thực sự nó ở bù đắp 4. Tại sao lại như vậy?

Ngoài ra, Foo thực sự chỉ có 4 + 4 + 1 = 9 byte dữ liệu. Trình biên dịch tự động thêm byte đệm vào cuối. Nhưng một lần nữa, cho một giá trị liên kết 8 byte, không nên nó pad đến một bội số của 8 chứ không phải là 4?

Bất kỳ làm rõ nào được đánh giá cao!

+0

Bạn có chắc 'int' của bạn chỉ là 4 byte không? Máy bạn đang chạy cái gì? –

+0

@Tony: Đây là ứng dụng 32 bit. Nếu một int là 8 byte thay vì 4, một Foo với hai trong số đó không thể chỉ có 12 byte.:-) –

Trả lời

7

Đoạn trích của bạn giải thích điều này, "tùy theo số nào nhỏ hơn". Trên nền tảng 32 bit, int là 4 byte. 4 nhỏ hơn 8. Vì vậy, nó có một liên kết 4-byte.

pack pragma làm cho mọi thứ được đóng gói, không được giải nén. Nó sẽ không pad trừ khi nó có một lý do để.

+0

Đợi một chút. Tôi có một cấu trúc Bar với một thành viên Foo. Vì vậy, tôi giả định rằng "kích thước của các thành viên" được gọi là kích thước của Foo, đó là 12. Bạn có nói rằng trình biên dịch thực sự tìm kiếm/bên trong/Foo và (có lẽ đệ quy) tìm kiếm thành viên lớn nhất của/nguyên thủy/loại? Nếu có, có một số tham chiếu (trực tuyến) cho hành vi này không? –

+1

Trình biên dịch biết yêu cầu căn chỉnh của mọi loại. Yêu cầu căn chỉnh cho một 'Foo' là 4. Nó không phải là loại lớn nhất, nhưng yêu cầu cho cấu trúc bên ngoài, thường sẽ là yêu cầu căn chỉnh lớn nhất của bất kỳ kiểu chứa nào, nhưng không phải lúc nào cũng vậy. (Nó không thể là kích thước của kiểu đầy đủ. Nếu không, một số nguyên theo sau là một cấu trúc 9.000 byte sẽ có gần 9.000 byte của padding.Đó chỉ là ngớ ngẩn.Các trang MSDN đang nói về các loại đơn giản có yêu cầu liên kết là bằng size.) –

+0

Cảm ơn, điều đó sẽ giải thích hành vi. - Về lập luận của bạn rằng nó không thể là kích thước cấu trúc đầy đủ: Tôi nghĩ đó chính là lý do cho quy tắc "nào nhỏ hơn". Cấu trúc 9.000 byte của bạn, lớn hơn 8 byte, sẽ được căn chỉnh với bội số của 8 thay vào đó, do đó lãng phí chỉ 4 byte thay vì 8,996. Vì vậy, với tôi, thuật toán MSDN thực sự có ý nghĩa. Quá tệ dường như chỉ là một nửa sự thật! –

1

Khi báo giá của bạn nói - để kiểm tra sự liên kết 8 byte, bạn cần 8 hoặc nhiều loại dữ liệu byte. Đây là một mẫu với một số loại kích thước rõ ràng. Ngoài ra, việc đặt phần tử nhỏ vào cuối sẽ không hiển thị phần đệm vì nó có thể bị bỏ từ phần cuối của cấu trúc.

#include <stdio.h> 
int 
main(int argc, char *argv[]) 
{ 
    struct S { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(push,1) 
    struct T { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
#pragma pack(pop) 
#pragma pack(push,8) 
    struct U { 
     __int64 a; 
     __int8 b; 
     __int64 c; 
    }; 
    struct B { 
     __int8 c; 
     struct U s; 
    }; 
#pragma pack(pop) 

    printf("S %d T %d U %d B %d\n", 
      sizeof(struct S), sizeof(struct T), 
      sizeof(struct U), sizeof(struct B)); 
    return 0; 
} 

Biên dịch này với VC 2010:

C:\src>cl -nologo -W3 -Od packing.c && packing.exe 
packing.c 
S 24 T 17 U 24 B 32 
4

Hãy ghi nhớ lý do tại sao những vấn đề sắp xếp ở nơi đầu tiên. Nó ở đó để cho phép CPU nhanh chóng đọc bộ nhớ mà không cần phải ghép các byte. CPU không bao giờ đọc một cấu trúc trong một gulp, nó chỉ truy cập các thành viên của nó. Vì vậy, thực tế là cấu trúc Foo là 12 byte là không quan trọng. Chỉ có sự liên kết của các thành viên quan trọng. Do không có thành viên Foo nào có yêu cầu căn chỉnh lớn hơn 4, thành viên của Bar.foo chỉ cần căn chỉnh đến 4.

Foo 12 byte thay vì 9 cũng có thể sử dụng giải thích. Trình biên dịch thêm 3 byte đệm vào cuối để một mảng Foo vẫn có các thành viên được căn chỉnh chính xác cho mỗi phần tử mảng.

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