Truyền con trỏ 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 47 trên 63.
Truyền struct theo giá trị phù hợp khi đọc dữ liệu, nhưng sẽ kém hiệu quả với struct lớn vì toàn bộ cấu trúc bị sao chép. Quan trọng hơn, bạn không thể sửa đổi struct ban đầu bên trong hàm. Đây là lúc việc truyền con trỏ đến struct trở nên cần thiết.
Khi truyền con trỏ đến một struct thay vì chính struct đó, hàm sẽ nhận địa chỉ bộ nhớ của struct gốc. Cách này có hai ưu điểm chính: hiệu quả hơn (không cần sao chép) và cho phép hàm sửa đổi dữ liệu gốc.
Cú pháp của hàm nhận con trỏ đến một struct là:
void functionName(struct StructureName *ptr) {
// Thân hàm (function) sử dụng ptr->member
}Bên trong hàm (function), bạn dùng toán tử mũi tên để truy cập và sửa đổi các thành viên của struct. Mọi thay đổi thông qua con trỏ đều tác động trực tiếp đến struct ban đầu trong hàm gọi, nên cách này rất phù hợp với những hàm cần cập nhật hoặc chuyển đổi dữ liệu của struct.
Kỹ thuật này rất quan trọng khi viết các chương trình C hiệu quả làm việc với cấu trúc dữ liệu phức tạp, vì nó kết hợp sức mạnh của con trỏ với cách tổ chức của các struct.
Thử thách
DễTạo một chương trình C minh họa cách truyền con trỏ đến struct cho hàm để sửa đổi dữ liệu hiệu quả. Chương trình của bạn cần:
- Định nghĩa một
structcó tênBankAccountvới các thành viên sau:- Một số nguyên
accountNumberđể lưu số tài khoản - Một mảng ký tự
ownerNamecó kích thước 30 để lưu tên chủ tài khoản - Một số thực
balanceđể lưu số dư tài khoản - Một số nguyên
transactionCountđể lưu số lượng giao dịch
- Một số nguyên
- Viết một hàm có tên
depositMoneyđể:- Nhận một con trỏ đến struct
BankAccountvà một số thực làm tham số - Cộng số tiền đó vào số dư tài khoản bằng toán tử mũi tên
- Tăng số lượng giao dịch thêm 1
- In ra
Deposit successful. New balance: [balance]
- Nhận một con trỏ đến struct
- Viết một hàm có tên
withdrawMoneyđể:- Nhận một con trỏ đến struct
BankAccountvà một số thực làm tham số - Kiểm tra xem số tiền rút có lớn hơn số dư hiện tại hay không
- Nếu không đủ tiền, in ra
Insufficient funds. Current balance: [balance] - Nếu đủ tiền, trừ số tiền khỏi số dư, tăng số lượng giao dịch và in ra
Withdrawal successful. New balance: [balance]
- Nhận một con trỏ đến struct
- Viết một hàm có tên
displayAccountđể:- Nhận một con trỏ đến struct
BankAccountlàm tham số - In thông tin tài khoản theo đúng định dạng sau:
Account Information:Account Number: [accountNumber]Owner: [ownerName]Balance: [balance]Transactions: [transactionCount]
- Nhận một con trỏ đến struct
- Trong hàm main:
- Tạo một biến
BankAccountcó tênaccount - Đọc số tài khoản, tên chủ tài khoản và số dư ban đầu từ đầu vào
- Đặt số lượng giao dịch thành 0
- Gọi
displayAccountđể hiển thị trạng thái tài khoản ban đầu - Đọc số tiền gửi và gọi
depositMoney - Đọc số tiền rút và gọi
withdrawMoney - Gọi
displayAccountđể hiển thị trạng thái tài khoản cuối cùng
- Tạo một biến
Thử thách này minh họa sức mạnh của việc truyền con trỏ đến struct cho hàm. Khác với cách truyền theo giá trị, các hàm nhận địa chỉ bộ nhớ của struct gốc, nhờ đó có thể sửa đổi trực tiếp dữ liệu ban đầu. Cách này hiệu quả hơn vì không cần sao chép, đồng thời cho phép các hàm thay đổi vĩnh viễn giá trị của struct trong hàm gọi.
Tự làm thử
#include <stdio.h>
#include <string.h>
// TODO: Định nghĩa struct BankAccount tại đây
// TODO: Viết hàm (function) depositMoney tại đây
// TODO: Viết hàm withdrawMoney tại đây
// TODO: Viết hàm displayAccount tại đây
int main() {
// Đọc đầu vào
int accountNum;
char ownerName[30];
float initialBalance;
float depositAmount;
float withdrawAmount;
scanf("%d", &accountNum);
scanf("%s", ownerName);
scanf("%f", &initialBalance);
scanf("%f", &depositAmount);
scanf("%f", &withdrawAmount);
// TODO: Tạo biến (variable) BankAccount và khởi tạo biến đó
// TODO: Gọi displayAccount để hiển thị trạng thái ban đầu
// TODO: Gọi depositMoney với số tiền gửi
// TODO: Gọi withdrawMoney với số tiền rút
// TODO: Gọi displayAccount để hiển thị trạng thái cuối 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