malloc cho struct và con trỏ trong C


84

Giả sử tôi muốn xác định cấu trúc đại diện cho độ dài của vectơ và các giá trị của nó là:

Bây giờ, giả sử tôi muốn xác định một vectơ y và cấp phát bộ nhớ cho nó.

Tìm kiếm của tôi trên internet cho thấy rằng tôi nên phân bổ bộ nhớ cho x một cách riêng biệt.

Nhưng, có vẻ như tôi đang cấp phát bộ nhớ cho y-> x hai lần, một trong khi cấp phát bộ nhớ cho y và một trong khi cấp phát bộ nhớ cho y-> x, và có vẻ như lãng phí bộ nhớ. Nó rất được đánh giá cao nếu cho tôi biết trình biên dịch thực sự làm gì và đâu sẽ là cách phù hợp để khởi tạo cả y và y-> x.

Cảm ơn trước.


5
Như đã chỉ ra rõ ràng bởi paxdiablo, vui lòng không bỏ giá trị trả về malloc()bằng C. Tôi sẽ không bao giờ hiểu tại sao mọi người lại cảm thấy cần phải làm như vậy. :(
thư giãn

14
@unwind, có lẽ họ đang cũ C lập trình viên ++ nâng cấp lên C :-)
paxdiablo

@unwind Khi sử dụng trình biên dịch nvcc của Nvidia trên mã C, nếu tôi không truyền kết quả của malloc, nó sẽ xuất hiện một lỗi.
Nubcake, 12/07/17

@Nubcake Theo liên kết này có thể là do nvcc chạy trình biên dịch cơ bản ở chế độ C ++, do giao diện CUDA của chúng là C ++. Trong C, bạn sẽ không nhận được lỗi cho điều này. Trong C ++ void *không tự động chuyển đổi sang các con trỏ khác, và ép kiểu là cần thiết (hoặc tất nhiên là không sử dụng malloc()trong C ++).
thư giãn

@unwind Yep, sau này tôi mới biết về điều này :) Chỉ muốn nêu một tình huống mà nếu bạn không truyền kết quả thì nó sẽ bị lỗi.
Nubcake vào

Câu trả lời:


155

Không, bạn không phân bổ bộ nhớ cho y->xhai lần.

Thay vào đó, bạn đang phân bổ bộ nhớ cho cấu trúc (bao gồm một con trỏ) cộng với một thứ gì đó để con trỏ trỏ tới.

Nghĩ theo cách này:

Vì vậy, bạn thực sự cần hai phân bổ ( 12) để lưu trữ mọi thứ.

Ngoài ra, kiểu của bạn nên là struct Vector *yvì nó là một con trỏ và bạn không bao giờ nên chuyển giá trị trả về từ malloctrong C vì nó có thể ẩn một số vấn đề nhất định mà bạn không muốn ẩn - C hoàn toàn có khả năng chuyển đổi ngầm void*giá trị trả về sang bất kỳ con trỏ nào khác.

Và, tất nhiên, bạn có thể muốn gói gọn việc tạo các vectơ này để giúp quản lý chúng dễ dàng hơn, chẳng hạn như với:

Bằng cách đóng gói việc tạo ra như vậy, bạn đảm bảo rằng các vectơ hoặc được tạo hoàn chỉnh hoặc không được xây dựng hoàn toàn - không có khả năng chúng được tạo một nửa. Nó cũng cho phép bạn thay đổi hoàn toàn các cấu trúc dữ liệu cơ bản trong tương lai mà không ảnh hưởng đến các máy khách (ví dụ: nếu bạn muốn làm cho chúng thành các mảng thưa thớt để đánh đổi không gian lấy tốc độ).


26
Run rẩy trước kỹ năng nghệ thuật ASCII của tôi :-)
paxdiablo

Cảm ơn rất nhiều. Thực sự hữu ích.
Pouya

1
Trong if (retval == NULL), retval nên retval
Legion Daeth

5

Lần đầu tiên, bạn cấp phát bộ nhớ cho Vector, có nghĩa là các biến x, n.

Tuy nhiên x vẫn chưa chỉ ra bất cứ điều gì hữu ích .

Vì vậy, đó là lý do tại sao phân bổ thứ hai cũng cần thiết .


3

Vài điểm

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector)); sai

nó phải được struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));kể từ khi ycon trỏ đến struct Vector.

1st malloc()chỉ cấp phát bộ nhớ đủ để chứa cấu trúc Vector (là con trỏ đến double + int)

Thứ 2 malloc()thực sự phân bổ bộ nhớ để giữ 10 đôi.


2

Bạn thực sự có thể làm điều này trong một malloc duy nhất bằng cách phân bổ cho Vectơ và mảng cùng một lúc. Ví dụ:

Điều này phân bổ Vector 'y', sau đó làm cho y-> x trỏ đến dữ liệu được cấp bổ sung ngay sau cấu trúc Vector (nhưng trong cùng một khối bộ nhớ).

Nếu cần thay đổi kích thước vectơ, bạn nên thực hiện với hai phân bổ như được khuyến nghị. Mảng y-> x bên trong sau đó sẽ có thể được thay đổi kích thước trong khi vẫn giữ nguyên cấu trúc vectơ 'y'.


Tại sao bạn lại gõ cast y thành char cụ thể? Tại sao không (double *) y + sizeof (struct Vector)?
Vishnu Prasath

sizeof trả về kích thước struct tính bằng byte và toán tử số học con trỏ '+' sẽ thêm vào con trỏ 'y' là bội số của sizeof ( y). Nếu chúng tôi làm như bạn đã làm ở trên, y sẽ được tăng lên theo sizeof (gấp đôi) * sizeof (struct), quá nhiều. Đúc y để char cho phép chúng ta tăng y bằng sizeof (char) * sizeof (struct) = 1 * sizeof (struct)
PQuinn

2

Về nguyên tắc, bạn đang làm đúng rồi. Đối với những gì bạn muốn, bạn cần hai malloc()giây.

Chỉ là một số nhận xét:

nên là

Trong dòng đầu tiên, bạn cấp phát bộ nhớ cho một đối tượng Vector. malloc()trả về một con trỏ đến bộ nhớ được cấp phát, vì vậy y phải là một con trỏ Vector. Trong dòng thứ hai, bạn phân bổ bộ nhớ cho một mảng 10 nhân đôi.

Trong C, bạn không cần các khuôn mẫu rõ ràng, và việc viết sizeof *ythay vào đó sizeof(struct Vector)là tốt hơn để đảm bảo an toàn khi nhập, và bên cạnh đó, nó tiết kiệm khi nhập.

Bạn có thể sắp xếp lại cấu trúc của mình và thực hiện một thao tác malloc()như sau:


"tiết kiệm khi nhập" không bao giờ là một đối số hợp lệ cho các quyết định lập trình. Lý do thực sự mà bạn sử dụng * y là vì lý do an toàn, đảm bảo rằng bạn phân bổ nhiều không gian cần thiết cho biến tương ứng.
Lundin

@Lundin Tôi đã cập nhật câu trả lời của tôi, nhưng đối với tôi là "an toàn kiểu" đối số là gần như trong cùng một giải đấu như viếtif(NULL == foo)
Wernsey

Bạn là người rao giảng để sử dụng sizeof *y. Nếu bạn làm điều đó chỉ vì mục đích gõ ít hơn 5 chữ cái (sizeof * y so với sizeof (Vector)) mà không có đối số nào khác, thì đó hẳn là vì bạn thấy nhiệm vụ gõ 5 chữ cái trên bàn phím của mình là một trở ngại lớn. Và nếu như vậy, thì có lẽ xem xét một lựa chọn nghề nghiệp, kể từ khi chương trình liên quan đến một toàn bộ rất nhiều bàn phím gõ ...
Lundin

@Lundin Viết sizeof *ygiúp bạn chống lại các lỗi như viết sizeof(Vector)khi bạn muốn sizeof(Matrix). Bạn có thường mắc những lỗi như vậy không? Bạn tìm và sửa chúng nhanh như thế nào khi bạn làm vậy? Tôi đồng ý rằng nó làm tăng độ an toàn của kiểu gõ và tôi viết sizeof *ybằng mã của riêng mình, nhưng nó gần như hoang tưởng như viết if(NULL == foo)để ngăn lỗi chính tả trên ==.
Wernsey

1
@ShmuelKamensky Chúng tương đương về mặt chức năng. ylà một con trỏ đến struct Vectorđể sizeof *yđược nói "kích thước của những gì điểm y", do đó sizeof struct Vector.
Wernsey

1

Khi bạn cấp phát bộ nhớ cho struct Vectorbạn, bạn chỉ cần cấp phát bộ nhớ cho con trỏ x, tức là cho khoảng trắng, nơi giá trị của nó, chứa địa chỉ, sẽ được đặt. Vì vậy, như vậy bạn không phân bổ bộ nhớ cho khối, mà y.xsẽ tham chiếu.


1

Đầu tiên malloc phân bổ bộ nhớ cho struct, bao gồm cả bộ nhớ cho x (con trỏ để nhân đôi). Malloc thứ hai cấp phát bộ nhớ cho giá trị kép mà x trỏ đến.


0

Khi bạn malloc(sizeof(struct_name))tự động phân bổ bộ nhớ cho kích thước đầy đủ của cấu trúc, bạn không cần phân bổ từng phần tử bên trong.

Sử dụng -fsanitize=addresscờ để kiểm tra cách bạn đã sử dụng bộ nhớ chương trình của mình.


Sai lầm. x chỉ là một con trỏ, bạn phải cấp phát bộ nhớ cho giá trị x trỏ tới. Đọc câu trả lời khác để biết thêm chi tiết. (ví dụ: stackoverflow.com/a/14768280/5441253 )
Maxime Ashurov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.