2013-08-01 33 views
10

Một cấu trúc với một thành viên mảng linh hoạt, rõ ràng, không có ý định khai báo, mà được sử dụng kết hợp với một con trỏ tới cấu trúc đó. Khi khai báo một thành viên mảng linh hoạt, phải có ít nhất một thành viên khác, và thành viên mảng linh hoạt phải là thành viên cuối cùng trong cấu trúc đó.Các thành viên mảng linh hoạt có thực sự cần thiết không?

Hãy nói rằng tôi có một mà trông như thế này:

struct example{ 
    int n; 
    int flm[]; 
} 

Sau đó, để sử dụng nó, tôi sẽ phải khai báo một con trỏ và sử dụng malloc để dành bộ nhớ cho nội dung của cấu trúc.

struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int)); 

Tức là, nếu tôi muốn mảng flm [] giữ năm số nguyên. Sau đó, tôi chỉ có thể sử dụng struct tôi như thế này:

ptr->flm[0] = 1; 

Câu hỏi của tôi là, tôi không nên có thể chỉ cần sử dụng một con trỏ thay vì điều này? Nó không chỉ tương thích trước C99, nhưng tôi có thể sử dụng nó có hoặc không có con trỏ đến cấu trúc đó. Xem xét tôi đã phải sử dụng malloc với flm, tôi không nên chỉ có thể làm điều này?

Hãy xem xét định nghĩa mới này về cấu trúc ví dụ;

struct example{ 
    int n; 
    int *notflm; 
} 

struct example test = {4, malloc(sizeof(int) * 5)}; 

Tôi thậm chí còn muốn có thể sử dụng thay thế cách tương tự như các thành viên mảng linh hoạt:

Sẽ điều này cũng làm việc? (Cung cấp định nghĩa ví dụ trên với notflm)

struct example test; 
test.n = 4; 
notflm = malloc(sizeof(int) * 5); 
+0

bản sao có thể có của [Thành viên mảng linh hoạt trong C - xấu?] (Http://stackoverflow.com/questions/246977/flexible-array-members-in-c-bad). Mảng linh hoạt là một phần của C99; con trỏ không và có thể có vấn đề về tính di động. –

+5

Sự khác biệt chính là các thành viên mảng linh hoạt cho phép bạn phân bổ toàn bộ cấu trúc trong một khối liền kề. Nếu bạn muốn sao chép cấu trúc, chỉ cần tính toán kích thước của nó và làm một bản sao. Trong khi với một con trỏ, bạn phải sao chép cấu trúc, nhưng sau đó cũng sao chép mảng bên trong. –

+1

@TedHopp theo cách có thể tưởng tượng nào bạn có thể nghiêm túc nói rằng con trỏ không phải là một phần của C99? –

Trả lời

23

Con trỏ không phải là mảng. Các lý do cơ bản để chọn sử dụng nào giống như chúng luôn có với các mảng so với con trỏ. Trong trường hợp đặc biệt của các thành viên mảng linh hoạt, dưới đây là một số lý do khiến bạn có thể thích chúng hơn một con trỏ:

  • Giảm yêu cầu lưu trữ. Một con trỏ sẽ mở rộng cấu trúc của bạn bằng (thông thường) 4 hoặc 8 byte và bạn sẽ chi tiêu nhiều hơn trong chi phí nếu bạn phân bổ lưu trữ được trỏ riêng biệt thay vì chỉ với một cuộc gọi đến malloc.

  • Cải thiện hiệu quả truy cập. Một thành viên mảng linh hoạt được đặt tại một offset không đổi từ cơ sở cấu trúc. Một con trỏ yêu cầu một tham số riêng biệt. Điều này ảnh hưởng đến cả hai hướng dẫn cần thiết để truy cập nó và đăng ký áp lực.

  • Nguyên nhân phân bổ thành công/thất bại. Nếu bạn phân bổ cấu trúc và phân bổ lưu trữ cho nó để trỏ đến hai bước riêng biệt, mã của bạn để làm sạch trong các trường hợp thất bại sẽ xấu hơn nhiều, vì bạn có trường hợp thành công và trường hợp khác không thành công. Điều này có thể tránh được với một số con trỏ số học để khắc cả hai trong cùng một yêu cầu malloc, nhưng thật dễ dàng để có được logic sai và gọi UB do các vấn đề liên kết.

  • Tránh cần phải sao chép sâu. Nếu bạn sử dụng một mảng linh hoạt thay vì một con trỏ, bạn có thể chỉ đơn giản là memcpy (không gán, vì nhiệm vụ không thể biết chiều dài mảng linh hoạt) để sao chép cấu trúc hơn là phải sao chép dữ liệu được trỏ đến và sửa con trỏ trong bản sao mới.

  • Tránh nhu cầu không có sâu.Nó rất tiện lợi và sạch sẽ để có thể chỉ free một đối tượng duy nhất thay vì phải free cũng được trỏ tới dữ liệu. Điều này cũng có thể đạt được với phương pháp "khắc lên một đơn malloc" được đề cập ở trên, tất nhiên, nhưng mảng linh hoạt giúp dễ dàng hơn và ít bị lỗi hơn.

  • Chắc chắn nhiều lý do hơn ...

+0

+1 danh sách các lý do hay, tôi đã tìm kiếm một bản tóm tắt tốt đẹp về những lợi ích cho [answer] này (http: // stackoverflow.com/a/20221073/1708801) nhưng tôi không tìm thấy bất cứ điều gì thực sự tuyệt vời, tôi sẽ liên kết đến câu trả lời này cũng như b/c này là tốt hơn so với bất cứ điều gì khác tôi tìm thấy cho đến nay. –

0

Những khái niệm này là chắc chắn không cần thiết như bạn đã chỉ ra chính mình.

Sự khác biệt giữa hai thứ bạn đã chứng minh là nơi dữ liệu của bạn nằm trong bộ nhớ.

Trong ví dụ đầu tiên với mảng linh hoạt, siêu dữ liệu của bạn và chính mảng đó nằm trong cùng một khối bộ nhớ và có thể được di chuyển dưới dạng một khối (con trỏ) nếu bạn cần.

Trong ví dụ thứ hai, siêu dữ liệu của bạn nằm trên ngăn xếp và mảng của bạn nằm ở nơi khác trên vùng heap. Để di chuyển/sao chép nó, bây giờ bạn sẽ cần phải di chuyển hai khối bộ nhớ và cập nhật con trỏ trong cấu trúc siêu dữ liệu của bạn.

Nói chung mảng kích thước linh hoạt được sử dụng khi bạn cần đặt một mảng và siêu dữ liệu của nó không gian với nhau trong bộ nhớ.

Ví dụ này hữu ích khi đặt một mảng với siêu dữ liệu trong tệp - bạn chỉ có một khối bộ nhớ liên tục và mỗi khi bạn tải nó sẽ (được đặt nhiều nhất) vị trí của máy ảo của bạn.

+1

Dữ liệu mảng có thể trong nhiều trường hợp được đặt ngay sau dữ liệu cấu trúc. Điều đó sẽ cho phép sử dụng một phân bổ duy nhất, * nhưng * bất kỳ lúc nào cấu trúc được tải, di chuyển hoặc sao chép, nó sẽ là cần thiết để cập nhật con trỏ. – supercat

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