2010-01-13 61 views
35

Tôi đã xem xét xung quanh nhưng không thể tìm thấy giải pháp cho câu hỏi hay. Đây là mã tôi có:Làm thế nào để bao gồm một mảng động INSIDE một cấu trúc trong C?

#include <stdlib.h> 

struct my_struct { 
    int n; 
    char s[] 
}; 

int main() 
{ 
    struct my_struct ms; 
    ms.s = malloc(sizeof(char*)*50); 
} 

và đây là gcc lỗi mang lại cho tôi: lỗi: sử dụng không hợp lệ của thành viên mảng linh hoạt

tôi có thể lấy nó để biên dịch nếu tôi tuyên bố tuyên bố s bên trong struct là

char* s 

và điều này có lẽ là một việc thực hiện vượt trội (con trỏ số học là nhanh hơn so với mảng, đúng không?) nhưng tôi nghĩ rằng trong khai ca của

012.
char s[] 

cũng giống như

char* s 
+6

'char s []' giống với 'char * s' chỉ bên trong danh sách tham số của hàm. –

Trả lời

59

Cách bạn có nó viết bây giờ, thường được gọi là "struct hack", cho đến khi C99 may mắn nó như là một "thành viên mảng linh hoạt". Lý do bạn đang nhận được một lỗi (có lẽ anyway) là nó cần phải được theo sau bởi một dấu chấm phẩy:

#include <stdlib.h> 

struct my_struct { 
    int n; 
    char s[]; 
}; 

Khi bạn phân bổ không gian cho điều này, bạn muốn phân bổ kích thước của struct cộng sự số lượng không gian bạn muốn cho mảng:

struct my_struct *s = malloc(sizeof(struct my_struct) + 50); 

Trong trường hợp này, thành viên mảng linh hoạt là mảng char và sizeof (char) == 1, do đó bạn không cần nhân với kích thước của nó , nhưng cũng giống như bất kỳ malloc nào khác mà bạn cần nếu đó là một loại của một số loại khác:

struct dyn_array { 
    int size; 
    int data[]; 
}; 

struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int)); 

Chỉnh sửa: Điều này cho kết quả khác với việc thay đổi thành viên thành con trỏ. Trong trường hợp đó, bạn (thông thường) cần hai phân bổ riêng biệt, một cho cấu trúc chính nó, và một cho dữ liệu "thêm" được trỏ đến bởi con trỏ. Sử dụng thành viên mảng linh hoạt, bạn có thể phân bổ tất cả dữ liệu trong một khối duy nhất.

+0

cảm ơn câu trả lời rất thông tin – Tom

+1

C99 thực sự cho phép điều đó - urghh! –

+3

Wow, tôi chưa bao giờ thấy điều này ... điều này dựa vào "linh hoạt mảng thành viên" là điều cuối cùng tuyên bố trong cấu trúc? Bạn có bị hạn chế chỉ có một "thành viên mảng linh hoạt" cho mỗi cấu trúc không? – vicatcu

0

Mảng sẽ giải quyết để con trỏ, và ở đây bạn phải xác định s như char *s. Cấu trúc cơ bản là một thùng chứa, và phải (IIRC) là kích thước cố định, do đó, có một mảng động có kích thước bên trong của nó chỉ đơn giản là không thể. Vì dù sao, bạn vẫn còn đang malloc nhập bộ nhớ, điều này sẽ không tạo ra bất kỳ sự khác biệt nào về những gì bạn đang theo dõi.

Về cơ bản bạn đang nói, s sẽ cho biết vị trí bộ nhớ. Lưu ý rằng bạn vẫn có thể truy cập sau này bằng cách sử dụng ký hiệu như s[0].

0

pointer arithmetic is faster than arrays, yes?

Không hề - chúng thực sự giống nhau. mảng dịch sang con trỏ arithmetics tại thời gian biên dịch.

char test[100]; 
test[40] = 12; 

// translates to: (test now indicates the starting address of the array) 
*(test+40) = 12; 
-6

mã được tạo sẽ giống hệt nhau (mảng và ptr). Ngoài thực tế là mảng một sẽ không biên dịch đó là

và BTW - làm điều đó C++ và sử dụng vector

+4

Gợi ý tôi sử dụng C++ và một vectơ không hề là một gợi ý mang tính xây dựng. Bạn có thể chỉ cần nói: sử dụng kỹ sư phần mềm để viết chương trình cho bạn – Tom

+0

Mã được tạo sẽ không đồng nhất từ ​​xa. Mảng không phải là một con trỏ. Nhúng mảng vào một cấu trúc hoặc trỏ đến một mảng từ một cấu trúc là hai thứ hoàn toàn khác nhau. – AnT

+0

vâng, đúng là chúng không giống nhau. Tôi đã cố gắng để nói rằng trong trường hợp mã đang làm việc trên một cái gì đó đã được chuyển cho nó như là một foo * hoặc một foo [] thì mã sẽ giống hệt nhau. Thực chất là không có sự khác biệt hoàn hảo – pm100

0

Việc sử dụng một mảng kích thước không xác định chỉ được phép ở cuối cấu trúc và chỉ hoạt động trong một số trình biên dịch. Nó là một phần mở rộng trình biên dịch không chuẩn. (Mặc dù tôi nghĩ rằng tôi nhớ C++ 0x sẽ cho phép điều này.)

Mảng sẽ không được phân bổ riêng biệt cho từ cấu trúc. Vì vậy, bạn cần phải phân bổ tất cả các my_struct, không chỉ là phần mảng.

Những gì tôi làm chỉ đơn giản là cung cấp cho mảng một kích thước nhỏ nhưng khác không. Thông thường 4 đối với mảng ký tự và 2 cho các mảng wchar_t để duy trì liên kết 32 bit.

Sau đó, bạn có thể lấy kích thước khai báo của mảng vào tài khoản, khi bạn thực hiện phân bổ. Tôi thường không dựa trên lý thuyết rằng slop nhỏ hơn mức độ chi tiết mà trình quản lý heap hoạt động trong mọi trường hợp.

Ngoài ra, tôi nghĩ bạn không nên sử dụng sizeof (char *) trong phân bổ của mình.

Đây là những gì tôi sẽ làm.

struct my_struct { 
    int nAllocated; 
    char s[4]; // waste 32 bits to guarantee alignment and room for a null-terminator 
}; 

int main() 
{ 
    struct my_struct * pms; 
    int cb = sizeof(*pms) + sizeof(pms->s[0])*50; 
    pms = (struct my_struct*) malloc(cb); 
    pms->nAllocated = (cb - sizoef(*pms) + sizeof(pms->s))/sizeof(pms->s[0]); 
} 
17

Bạn cần quyết định bạn đang cố gắng làm gì trước tiên.


Nếu bạn muốn có một cấu trúc với một con trỏ đến một [độc lập] mảng bên trong, bạn phải khai báo nó như

struct my_struct { 
    int n; 
    char *s; 
}; 

Trong trường hợp này bạn có thể tạo ra các đối tượng struct thực tế trong bất kỳ cách bạn hài lòng (như một biến tự động, ví dụ)

struct my_struct ms; 

và sau đó phân bổ bộ nhớ cho mảng độc lập

ms.s = malloc(50 * sizeof *ms.s); 

Trong thực tế, không có nhu cầu chung để phân bổ bộ nhớ mảng động

struct my_struct ms; 
char s[50]; 

ms.s = s; 

Tất cả phụ thuộc vào loại đời bạn cần từ các đối tượng này. Nếu cấu trúc của bạn là tự động, thì trong hầu hết các trường hợp mảng cũng sẽ tự động. Nếu đối tượng struct sở hữu bộ nhớ mảng, đơn giản là không có điểm nào khi thực hiện khác. Nếu bản thân cấu trúc là động, thì mảng cũng thường là động.

Lưu ý rằng trong trường hợp này, bạn có hai khối bộ nhớ độc lập: cấu trúc và mảng.


Cách tiếp cận hoàn toàn khác sẽ là sử dụng thành ngữ "struct hack". Trong trường hợp này mảng trở thành một phần không tách rời của cấu trúc. Cả hai đều nằm trong một khối bộ nhớ. Trong C99 struct sẽ được khai báo là

struct my_struct { 
    int n; 
    char s[]; 
}; 

và để tạo ra một đối tượng bạn phải phân bổ toàn bộ điều động

struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s); 

Kích thước của khối bộ nhớ trong trường hợp này được tính toán để thích ứng với cấu trúc thành viên và mảng đuôi của kích thước thời gian chạy.

Lưu ý rằng trong trường hợp này bạn không có tùy chọn để tạo các đối tượng struct như đối tượng tĩnh hoặc tự động. Các cấu trúc với các thành viên mảng linh hoạt ở cuối chỉ có thể được phân bổ động trong C.


Giả định của bạn về aritmetics nhanh hơn là mảng hoàn toàn không chính xác. Mảng hoạt động thông qua con trỏ arithmetics theo định nghĩa, vì vậy về cơ bản chúng giống nhau. Hơn nữa, một mảng chính hãng (không bị phân rã thành con trỏ) thường nhanh hơn một chút so với đối tượng con trỏ. Giá trị con trỏ phải được đọc từ bộ nhớ, trong khi vị trí của mảng trong bộ nhớ là "đã biết" (hoặc "được tính toán") từ chính đối tượng mảng đó.

0

Tôi nghi ngờ trình biên dịch không biết cần bao nhiêu không gian để cấp phát cho s [], nếu bạn chọn khai báo một biến tự động với nó.

Tôi đồng tình với những gì Ben cho biết, tuyên bố struct của bạn

struct my_struct { 
    int n; 
    char s[1]; 
}; 

Ngoài ra, để làm rõ ý kiến ​​của mình về lưu trữ, tuyên bố char *s sẽ không đưa các cấu trúc trên stack (vì nó tự động được phân bổ) và phân bổ s trong heap, những gì nó sẽ làm là giải thích các byte sizeof(char *) đầu tiên của mảng của bạn như là một con trỏ, vì vậy bạn sẽ không hoạt động trên dữ liệu bạn nghĩ bạn đang có, và có lẽ sẽ gây tử vong.

Điều quan trọng cần nhớ là mặc dù các thao tác trên con trỏ và mảng có thể được triển khai theo cùng một cách, chúng không giống nhau.

0

Có rất nhiều câu trả lời liên quan đến mảng linh hoạt C99.

Tôi muốn nhận xét về câu trả lời của Alexander Gessler về con trỏ giống như mảng.

Chúng không phải; Mảng là một biểu thức, con trỏ là một biến.

Chúng KHÔNG có sự khác biệt tinh tế đặc biệt là khi di chuyển qua lượng dữ liệu LỚN. Đôi khi bạn cần phải siết chặt mọi mSec (tôi làm việc trên các hệ thống đồ họa nhúng).

+0

vì vậy nhanh hơn? –

+1

Con trỏ có thể hơi nhanh hơn hoặc nhanh hơn tùy thuộc vào trình biên dịch và kiến ​​trúc.Nó được biên dịch như một tải đơn giản từ hướng dẫn bộ nhớ. Một mảng có thể là bất kỳ nơi nào từ một lệnh máy đơn lẻ hoặc một số lệnh máy. Quan điểm của tôi là chúng không dẫn đến mã máy và có thể thay đổi về hiệu suất. – Fred

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