2010-06-15 39 views
5

Trích dẫn từ phần C-std 6.7.2.1,viên mảng linh hoạt trong C-cấu trúc

struct s { int n; double d[]; }; 

Đây là một tuyên bố cấu trúc hợp lệ. Tôi đang tìm kiếm một số sử dụng thực tế của loại cú pháp này. Nói một cách chính xác, cấu trúc này mạnh hơn hoặc ít hơn so với việc giữ nguyên gấp đôi * làm yếu tố thứ 2? Hay đây là trường hợp khác của 'bạn-có-làm-nó-theo-nhiều-cách'?

Arpan

+2

Ah, đây là một ví dụ điển hình cho thấy rằng các mảng và con trỏ không giống nhau :) – fredoverflow

Trả lời

10

C FAQ trả lời chính xác câu hỏi này. Câu trả lời nhanh là cấu trúc này sẽ bao gồm các mảng double bên trong cấu trúc chứ không phải là một con trỏ tới một mảng bên ngoài cấu trúc. Như một ví dụ nhanh chóng, bạn có thể sử dụng cấu trúc của bạn như trong ví dụ này:

struct s mystruct = malloc(sizeof(struct s) + 5 * sizeof(double)); 
s.n = 12; 
s.d[0] = 4.0; 
s.d[1] = 5.0; 
s.d[2] = 6.0; 
s.d[3] = 7.0; 
s.d[4] = 8.0; 

Và vân vân - kích thước của mảng mà bạn quan tâm được bao gồm trong việc phân bổ, và sau đó bạn có thể sử dụng nó giống như bất kỳ mảng . Thông thường một kiểu như vậy có chứa kích thước như một phần của cấu trúc, vì việc sử dụng thủ thuật + để bỏ qua một mảng kiểu s sẽ nhất thiết phải phức tạp bởi tình huống này.

Để thêm câu hỏi của bạn 'làm thế nào là xây dựng này nhiều hơn hoặc ít hơn mạnh mẽ hơn giữ [con trỏ] làm yếu tố thứ 2?', Nó không còn mạnh mẽ mỗi se, nhưng bạn không cần phải giữ một con trỏ xung quanh, vì vậy bạn sẽ tiết kiệm được ít nhất không gian đó - cũng như khi bạn sao chép cấu trúc, bạn cũng sẽ sao chép mảng, chứ không phải là con trỏ tới mảng - một sự khác biệt tinh tế đôi khi, nhưng rất quan trọng vào những thời điểm khác. 'Bạn có thể-làm-nó-trong-nhiều cách' có lẽ là một lời giải thích tốt, nhưng có những trường hợp mà bạn đặc biệt muốn một thiết kế hay cái kia.

+0

do đó struct s s1 = malloc (...); và sau đó struct s s2 = s1; có nghĩa là s2 nhận được một mảng được tạo tự động và nội dung của s1 được sao chép?không giữ cùng nếu thay vì các loại POD struct s có lớp người dùng định nghĩa là yếu tố thứ 2? – Fanatic23

+0

Không, không có phép sao chép ma thuật nào xảy ra với việc gán cấu trúc; nhưng nếu bạn sử dụng 'memcpy()' với kích thước thích hợp, nó sẽ hoạt động. Nếu bạn có một con trỏ, bạn sẽ cần phải cấp phát bộ nhớ và sao chép mảng một cách riêng biệt. –

+1

Tôi không chắc chắn rằng liên kết đến C FAQ, q2.6, thực sự trả lời câu hỏi này cả. Nếu có, nó chỉ là một ý nghĩa phức tạp mà chỉ có ý nghĩa với một người đã biết câu trả lời. Trong thực tế, liên kết gợi ý điều này, nếu nó đang nói về cùng một điều, không được coi là di động. – BobbyShaftoe

0

Tôi đã thấy điều này được sử dụng trên Windows cho các chuỗi được gắn thẻ theo độ dài của chúng. Dữ liệu ký tự được lưu trữ trực tiếp sau độ dài trong bộ nhớ, giữ mọi thứ gọn gàng với nhau.

typedef struct { 
    SIZE_T bytes; 
    TCHAR chars[]; 
} tagged_string; 
1

Bạn có thể sử dụng nó để thêm các trường tiêu đề để cấp phát động mảng, phổ biến nhất một trong số đó sẽ là kích thước của nó:

struct int_array 
{ 
    size_t size; 
    int values[]; 
}; 

struct int_array *foo = malloc(sizeof *foo + 42 * sizeof *foo->values); 
foo->size = 42; 

... 

for(size_t i = 0; i < foo->size; ++i) 
    foo->values[i] = i * i; 

Bạn có thể đạt được kết quả tương tự bằng cách sử dụng một viên int * thay vào đó và phân bổ mảng một cách riêng biệt, nhưng nó sẽ kém hiệu quả hơn cả về mặt bộ nhớ (con trỏ bổ sung, quản lý heap cho khối bộ nhớ thứ 2) và thời gian chạy (bổ sung thêm, phân bổ lần 2).

4

Ưu điểm chính là thành viên mảng linh hoạt cho phép bạn phân bổ một khối đơn cho mảng cùng với các dữ liệu khác trong cấu trúc (với con trỏ, bạn thường kết thúc bằng hai phân bổ riêng biệt khối).

Nó cũng hữu ích với dữ liệu được truyền bởi một vài giao thức mạng, nơi luồng đến được xác định theo cùng một cách - một số nguyên xác định độ dài, theo sau là nhiều đơn vị (thường là byte/octet) dữ liệu. Bạn có thể (thường) sử dụng một loại-pun để phủ một cấu trúc với một thành viên mảng linh hoạt vào một bộ đệm chứa đầy dữ liệu đó, và làm việc trực tiếp thay vì phải phân tích nó thành từng phần và sau đó làm việc với các phần riêng lẻ.

+0

Theo kinh nghiệm của tôi, thực hiện một giao thức mạng (hoặc một định dạng tệp, về cơ bản là cùng một vấn đề) bằng cách gõ-punning một bộ đệm byte vào một kiểu cấu trúc thường là trường hợp bạn đang làm việc đó sai. Deserialising nó từng lĩnh vực thay vì kết thúc được nhiều hơn nữa di động. – caf

+0

@caf: Deserializing trường theo lĩnh vực di động hơn, nhưng gõ punning có thể trong một số trường hợp cho phép mã dễ đọc hơn và hiệu quả hơn, đặc biệt nếu nó có thể xây dựng một bảng con trỏ đến những thứ được lưu trữ trong bộ đệm hiện có, thay vì phải phân bổ không gian cho một bản sao thứ hai của tất cả các thông tin và sau đó sao chép tất cả các thông tin từ bộ đệm của byte vào không gian mới được phân bổ. Điều gì sẽ khiến mọi thứ thực sự dễ dàng là nếu C hỗ trợ cấu trúc "bố cục rõ ràng", vì vậy mã có thể nói, ví dụ: "Tôi cần kiểu dữ liệu 64 byte, có thể được đặt ... – supercat

+0

... trên bất kỳ ranh giới 2 byte nào và bao gồm [số khác] một số nguyên 32 bit có tên" Woozle "được lưu trữ ở độ lệch 12 dưới dạng bốn octet theo thứ tự nhỏ. " Có một trình biên dịch hỗ trợ điều đó và xử lý nó một cách hiệu quả trong trường hợp nó trùng với bố cục tự nhiên của trình biên dịch sẽ rẻ hơn việc cố gắng nhận biết và tối ưu hóa tất cả các biến thể khác nhau trên '(((uint32_t) ptr [15] << 24) | ((uint32_t) ptr [14] << 16) | ((uint32_t) ptr [13] << 8) | ptr [12]) 'có thể được thay thế bằng một vài tải 16 bit từ địa chỉ ptr + 12 và ptr + 14 hoặc một tải 32 bit duy nhất từ ​​ptr + 12. – supercat

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