Cấp phát động cho cấu trúc
Thuộc phần Logic và luồng điều khiển trong hành trình C của Coddy. Bài học 48 trên 63.
Đến nay, bạn đã học cách tạo struct trên ngăn xếp, chúng tồn tại trong phạm vi khai báo và được tự động dọn dẹp khi phạm vi đó kết thúc. Nhưng nếu bạn cần một struct tồn tại lâu hơn phạm vi ban đầu, hoặc không biết trước khi biên dịch mình sẽ cần bao nhiêu struct thì sao?
Đây là lúc cấp phát động phát huy tác dụng. Bạn có thể dùng malloc() để tạo các struct trên heap, giống như với mọi kiểu dữ liệu khác. Điểm khác biệt chính là các struct được cấp phát động vẫn nằm trong bộ nhớ cho đến khi bạn giải phóng chúng bằng free().
Đây là cú pháp để cấp phát động một struct:
struct Point *ptr = malloc(sizeof(struct Point));Lệnh này cấp phát đủ bộ nhớ trên heap để chứa một cấu trúc (struct) Point và trả về con trỏ đến vùng nhớ đó. Sau đó, bạn có thể dùng toán tử mũi tên để truy cập và sửa đổi các thành viên của cấu trúc:
ptr->x = 10;
ptr->y = 20;Luôn kiểm tra xem malloc() có thành công hay không (có trả về giá trị khác NULL không), và gọi free(ptr) khi dùng xong struct để tránh rò rỉ bộ nhớ. Kỹ thuật này rất cần thiết để xây dựng các chương trình linh hoạt có thể tạo và quản lý cấu trúc dữ liệu trong thời gian chạy.
Thử thách
DễTạo một chương trình C minh họa cách cấp phát động cho cấu trúc bằng malloc(). Chương trình cần:
- Định nghĩa một cấu trúc
structtên làCarvới các thành viên sau:- Một số nguyên
yearđể lưu năm sản xuất - Một mảng ký tự
brandcó kích thước 20 để lưu thương hiệu xe - Một mảng ký tự
modelcó kích thước 25 để lưu mẫu xe - Một số thực
priceđể lưu giá xe - Một số nguyên
mileageđể lưu quãng đường xe đã đi
- Một số nguyên
- Trong hàm main, khai báo một con trỏ đến cấu trúc
Carcó têncarPtr - Dùng
malloc()để cấp phát động bộ nhớ cho một cấu trúcCarvà gán địa chỉ trả về chocarPtr - Kiểm tra việc cấp phát bộ nhớ có thành công không:
- Nếu
carPtrlàNULL, in raMemory allocation failedrồi thoát chương trình - Nếu cấp phát thành công, in ra
Memory allocation successful
- Nếu
- Đọc các giá trị đầu vào sau và gán chúng cho các thành viên của cấu trúc bằng toán tử mũi tên:
- Đọc một số nguyên cho năm và gán bằng
carPtr->year - Đọc một chuỗi cho thương hiệu và gán bằng
carPtr->brand - Đọc một chuỗi cho mẫu xe và gán bằng
carPtr->model - Đọc một số thực cho giá và gán bằng
carPtr->price - Đọc một số nguyên cho quãng đường đã đi và gán bằng
carPtr->mileage
- Đọc một số nguyên cho năm và gán bằng
- Sau khi đọc tất cả giá trị, thực hiện các phép tính sau bằng toán tử mũi tên:
- Tính tuổi của xe bằng cách lấy 2024 trừ đi năm:
int age = 2024 - carPtr->year; - Tính tỷ lệ khấu hao dựa trên tuổi xe: nếu tuổi xe lớn hơn 10, đặt khấu hao là 0.6, nếu không thì đặt là 0.8
- Tính giá trị hiện tại:
carPtr->price * depreciation
- Tính tuổi của xe bằng cách lấy 2024 trừ đi năm:
- In thông tin xe bằng toán tử mũi tên theo đúng định dạng sau:
Car Information:Year: [year]Brand: [brand]Model: [model]Original Price: [price]Mileage: [mileage]Age: [age] yearsCurrent Value: [current_value]
- Giải phóng bộ nhớ đã cấp phát động bằng
free(carPtr) - In ra
Memory freed successfully
Thử thách này kiểm tra mức độ hiểu của bạn về cấp phát động cho cấu trúc bằng malloc(), kiểm tra lỗi cấp phát, truy cập các thành viên của cấu trúc thông qua con trỏ bằng toán tử mũi tên và giải phóng đúng cách bộ nhớ đã cấp phát bằng free(). Bạn sẽ thực hành toàn bộ vòng đời quản lý bộ nhớ động với cấu trúc.
Tự làm thử
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// TODO: Định nghĩa struct Car tại đây
int main() {
// TODO: Khai báo con trỏ đến struct Car có tên carPtr
// TODO: Dùng malloc() để cấp phát bộ nhớ cho một struct Car
// TODO: Kiểm tra xem việc cấp phát bộ nhớ có thành công không
// Đọc các giá trị đầu vào
int year, mileage;
char brand[20], model[25];
float price;
scanf("%d", &year);
scanf("%s", brand);
scanf("%s", model);
scanf("%f", &price);
scanf("%d", &mileage);
// TODO: Gán các giá trị đầu vào cho các thành viên của struct bằng toán tử mũi tên
// TODO: Tính tuổi xe, mức khấu hao và giá trị hiện tại
// TODO: In thông tin xe theo định dạng yêu cầu
// TODO: Giải phóng bộ nhớ đã cấp phát và in thông báo thành công
return 0;
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Logic và luồng điều khiển
1Nền tảng về con trỏ
Con trỏ là gì?Khai báo con trỏToán tử lấy địa chỉ (&)Toán tử giải tham chiếu (*)Con trỏ NULLÔn tập: kiến thức cơ bản về con trỏ4Dự án: Công cụ xử lý văn bản đơn giản
Tổng quan dự ánĐếm ký tự2Con trỏ và mảng
Tên mảng dưới dạng con trỏPhần tử mảng và con trỏPhép toán con trỏSo sánh con trỏÔn tập: duyệt mảng bằng con trỏ5Con trỏ và hàm
Truyền theo giá trịTruyền con trỏ vào hàmSửa đổi biến qua con trỏVí dụ kinh điển: hoán đổiTruyền mảng vào hàmÔn tập: đối số con trỏ hàm8Cấu trúc và con trỏ
Con trỏ đến cấu trúcToán tử mũi tên (->)Truyền cấu trúc theo giá trịTruyền con trỏ cấu trúcCấp phát động cho cấu trúcÔn tập: sửa đổi cấu trúc bằng con trỏ3Mảng ký tự và chuỗi
Chuỗi dưới dạng mảng charKý tự kết thúc nullNhập chuỗi bằng scanfDùng strlen()Dùng strcpy()Dùng strcat()Dùng strcmp()Ôn tập: các hàm xử lý chuỗi cơ bản6Quản lý bộ nhớ
Bộ nhớ ngăn xếp và bộ nhớ heapCấp phát động với malloc()Dùng sizeof() để cấp phátKiểm tra lỗi cấp phátGiải phóng bộ nhớ với free()Cấp phát với calloc()Ôn tập: Mảng động9Dự án: Nhập thông tin liên hệ đơn giản
Định nghĩa struct ContactHàm tạo liên hệTự luyện tập: Trình biên dịch C online