2011-02-02 27 views
8

Tôi muốn lấy một phần dữ liệu bên trong một số cấu trúc C để sắp xếp từng phần/deserialize chúng, ghi byte từ bộ nhớ vào đĩa và ngược lại.sizeof() một phần của cấu trúc C - loại

Các cấu trúc không được biết trước, chúng được tạo động bằng trình tạo mã C của riêng tôi (cũng như mã sẽ tuần tự hóa nó). Các trường tuần tự sẽ được đặt ở đầu cấu trúc.

Hãy nói rằng một có một cấu trúc với 4 lĩnh vực, và hai đầu tiên sẽ được đăng:

typedef struct { 
    int8_t x1; 
    int32_t x2; /* 1 + 4 = 5 bytes (if packed) */ 
    int8_t y1; 
    int32_t y2; /* 1 + 4 +1 + 4 = 10 bytes (if packed) */ 
} st; 

tôi dự định lấy con trỏ vào biến struct và viết/đọc n byte bao gồm những hai trường đầu tiên (x1, x2). Tôi không nghĩ rằng tôi cần phải lo lắng về sự liên kết/đóng gói bởi vì tôi không có ý định serialization để tồn tại compilations khác nhau (chỉ có một thực thi duy nhất dự kiến ​​sẽ đọc/ghi dữ liệu). Và, khi tôi đang nhắm mục tiêu một phạm vi rộng của các kiến ​​trúc trình biên dịch, tôi không muốn đặt các giả định về thủ thuật đóng gói liên kết hoặc trình biên dịch cụ thể.

Sau đó, tôi cần tính số byte. Và tôi không thể chỉ làm sizeof(st.x1)+sizeof(st.x2) vì việc làm đệm lót. Vì vậy, tôi đang lập kế hoạch để trừ con trỏ, từ khi bắt đầu cấu trúc đến trường "không liên tục" đầu tiên:

st myst; 
int partsize = (char*)&myst.y1 - (char*)(&myst); 
printf("partial size=%d (total size=%d)\n",partsize,sizeof(myst)); 

Điều này dường như hoạt động. Và nó có thể được đặt trong một macro.

(Đối với bản ghi: Tôi đã thử viết một macro khác không trả lại một thể hiện của cấu trúc, chẳng hạn như this, nhưng dường như không thể ở đây - nhưng điều này không quan trọng đối với tôi nhiều).

Câu hỏi của tôi: Điều này có chính xác và an toàn không? Bạn có thể thấy bất kỳ cạm bẫy tiềm năng nào, hoặc một số cách tiếp cận tốt hơn?

Trong số những thứ khác: Các tiêu chuẩn C (và trình biên dịch thực tế) có giả định rằng các trường cấu trúc nằm trong bộ nhớ theo cùng thứ tự như chúng được định nghĩa trong nguồn không? Điều này có lẽ là một câu hỏi ngu ngốc, nhưng tôi muốn chắc chắn ...

UPDATE: Một số kết luận từ các câu trả lời và những phát hiện của riêng tôi:

  1. Có vẻ là không có vấn đề với cách tiếp cận của tôi . Đặc biệt, C quy định rằng các trường struct sẽ không bao giờ thay đổi thứ tự.

  2. Một cũng có thể (như được đề xuất bằng số aswer) từ trường liên tục cuối cùng và thêm kích thước của nó: (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst). Điều đó sẽ tương đương, ngoại trừ việc nó sẽ bao gồm không phải byte đệm (nếu có) cho trường cuối cùng. Một lợi thế rất nhỏ - và một nhược điểm rất nhỏ, trong việc ít đơn giản hơn.

  3. Nhưng câu trả lời được chấp nhận, với offsetof, dường như thích hợp hơn đề xuất của tôi. Đó là thời gian biên dịch rõ ràng và thuần khiết, nó không đòi hỏi một thể hiện của cấu trúc. Nó tiếp tục có vẻ là tiêu chuẩn, có sẵn trong bất kỳ trình biên dịch. Nếu người ta không cần một cấu trúc biên dịch thời gian, và có một thể hiện của cấu trúc có sẵn (như là kịch bản của tôi) cả hai giải pháp đều tương đương nhau.

+0

Câu hỏi liên quan: http://stackoverflow.com/questions/2748995/c-struct-memory-layout – payne

+0

Tôi đã xóa thẻ C++, vì không có C++ ở bất kỳ đâu gần câu hỏi này. – Puppy

Trả lời

10

Bạn đã xem cơ sở offsetof chưa? Nó trả về độ lệch của một thành viên từ đầu của một cấu trúc. Vì vậy, offsetof (st, x2) trả về giá trị bù trừ của x2 từ đầu cấu trúc. Vì vậy, trong ví dụ của bạn offsetof (st, x2) + sizeof(st.x2) sẽ cung cấp cho bạn số lượng byte của các thành phần được tuần tự hóa.

này là khá giống với những gì bạn đang làm gì bây giờ, bạn chỉ nhận được để bỏ qua các padding sau x2 và sử dụng một mảnh ít được sử dụng của C.

+0

+1: Đánh bại tôi với nó :) – Lars

+0

Đề xuất tuyệt vời, không biết (hoặc gọi lại?) Nó. Đáng buồn thay, có vẻ như không có mặt ở khắp nơi. MINGW gcc, đặc biệt, thiếu nó. Nhưng nhìn trộm định nghĩa của nó trong các trình biên dịch khác có giá trị lớn. – leonbloy

+0

@leonbloy: 'offsetof()' là tiêu chuẩn C, nhưng bạn phải bao gồm '' để có được nó. – caf

0

Bạn có thể xem protobuf; nó dường như làm những gì bạn muốn một cách di động.

5

C đảm bảo loại hành vi này. Nó được thiết kế để cho phép đa hình nguyên thủy. Hãy xem xét:

struct X { 
    int a; 
}; 
struct Y { 
    int a; 
    int b; 
}; 
void foo(X* x) { 
    x->a = 10; 
}; 
Y y; 
foo((X*)&y); // Well defined behaviour- y.a = 10. 
3

Trình chỉnh sửa C có thể chèn byte đệm cho căn chỉnh nhưng không thể sắp xếp lại các biến cấu trúc.

Một cách rõ ràng hơn có thể chỉ là xác định cấu trúc thứ 2 cho các mục đích sizeof(), bao gồm các biến bắt đầu của cấu trúc bạn muốn. Trình biên dịch sẽ đảm bảo rằng 2 thanh chống có cùng biến trong cùng một thứ tự sẽ bố trí theo cùng một cách.

+0

+1 Đề xuất hay. Nhưng trong các kịch bản của tôi về các cấu trúc với nhiều trường - aproach này sẽ dẫn đến quá xâm lấn (nếu tôi xác định lại cấu trúc, lồng ghép) hoặc giới thiệu quá nhiều lộn xộn (nếu tôi tạo một cấu trúc đôi cho mỗi bản gốc). – leonbloy

0

Tôi bỏ phiếu cho nguyên tắc KISS. Viết vào phần tử tệp theo phần tử và bạn được đảm bảo không phụ thuộc trình biên dịch.

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