Con trỏ và giá trị
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 9 trên 61.
Khi truyền một struct vào hàm (function), C cho bạn hai lựa chọn: truyền theo giá trị (sao chép toàn bộ struct) hoặc truyền theo con trỏ (chỉ sao chép địa chỉ). Lựa chọn này ảnh hưởng đáng kể đến hiệu năng.
Truyền theo giá trị tạo một bản sao hoàn chỉnh của struct trên ngăn xếp. Với struct nhỏ chỉ có vài số nguyên thì cách này ổn. Nhưng hãy tưởng tượng một struct có kích thước hàng trăm byte: mỗi lần gọi hàm, chương trình sao chép toàn bộ dữ liệu đó:
typedef struct {
char name[100];
int scores[50];
double average;
} Student;
// Truyền theo giá trị: sao chép khoảng 308 byte mỗi lần gọi
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average);
}
// Truyền qua con trỏ: chỉ sao chép 8 byte (địa chỉ)
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average);
}
Cú pháp cũng khác nhau. Với tham số là giá trị, bạn dùng toán tử dấu chấm (s.name). Với con trỏ, bạn dùng toán tử mũi tên (s->name).
Phiên bản dùng con trỏ hiệu quả hơn vì chỉ truyền một địa chỉ bộ nhớ, thường là 8 byte bất kể kích thước của struct.
Đây là lý do các lập trình viên C gần như luôn truyền cấu trúc bằng con trỏ. Dùng const khi hàm không nên sửa đổi dữ liệu, và dùng con trỏ thông thường khi cần sửa đổi. Cách này vừa hiệu quả vừa thể hiện rõ ý định.
Thử thách
DễHãy tạo một module Product để minh họa sự khác biệt giữa việc truyền struct theo giá trị và truyền theo con trỏ. Bạn sẽ tạo hai hàm hiển thị: một hàm nhận toàn bộ struct dưới dạng bản sao, còn hàm kia chỉ nhận con trỏ đến struct đó.
Bạn sẽ tạo ba tệp:
product.h: Khai báo một structProductcó ba thành viên:char name[50],double pricevàint quantity. Đồng thời khai báo hai hàm:display_by_value: nhận mộtProducttheo giá trị và in thông tin của nódisplay_by_pointer: nhận mộtconst Product *và in cùng thông tin đó
PRODUCT_H.product.c: Triển khai cả hai hàm. Lưu ý sự khác biệt về cú pháp: phiên bản truyền theo giá trị dùng toán tử dấu chấm (p.name), còn phiên bản truyền con trỏ dùng toán tử mũi tên (p->name). Cả hai hàm cần in theo cùng một định dạng.main.c: Tạo một Product, sau đó gọi cả hai hàm hiển thị để cho thấy chúng tạo ra đầu ra giống hệt nhau dù dùng các kiểu tham số khác nhau.
Bạn sẽ nhận ba đầu vào: tên sản phẩm (một chuỗi), giá (một số thực) và số lượng (một số nguyên).
Trong tệp main, khởi tạo một Product với các giá trị này, sau đó gọi display_by_value rồi gọi display_by_pointer.
Cả hai hàm cần in theo định dạng này:
Product: {name}, Price: {price}, Qty: {quantity}Dùng %.2f cho giá để hiển thị hai chữ số thập phân.
Ví dụ, với các đầu vào Laptop, 999.99 và 5, đầu ra sẽ là:
Product: Laptop, Price: 999.99, Qty: 5
Product: Laptop, Price: 999.99, Qty: 5Tự làm thử
#include <stdio.h>
#include <string.h>
#include "product.h"
int main() {
char name[50];
double price;
int quantity;
// Đọc dữ liệu đầu vào
scanf("%s", name);
scanf("%lf", &price);
scanf("%d", &quantity);
// TODO: Tạo biến Product và khởi tạo bằng các giá trị đầu vào
// Gợi ý: Dùng strcpy để sao chép tên vào trường name của struct
// TODO: Gọi display_by_value với Product
// TODO: Gọi display_by_pointer với con trỏ trỏ đến Product
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 Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online