Hoán đổi tổng quát
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 51 trên 61.
Một thao tác phổ biến trong lập trình là hoán đổi hai giá trị. Thông thường, bạn sẽ viết một hàm hoán đổi riêng cho từng kiểu dữ liệu: một hàm cho int, một hàm khác cho double và cứ thế. Nhưng với void* và memcpy, mình có thể viết một hàm duy nhất để hoán đổi hai biến bất kỳ.
Điều quan trọng cần hiểu là việc hoán đổi chỉ đơn giản là di chuyển các byte. Nếu biết kích thước dữ liệu, bạn có thể sao chép các byte mà không cần biết kiểu dữ liệu thực tế. Chữ ký hàm như sau:
void generic_swap(void* a, void* b, size_t size);Tham số size cho hàm biết cần di chuyển bao nhiêu byte. Bên trong, ta dùng một vùng đệm tạm thời và memcpy từ <string.h> để hoán đổi các byte:
void generic_swap(void* a, void* b, size_t size) {
char temp[size]; // Bộ đệm tạm
memcpy(temp, a, size); // temp = a
memcpy(a, b, size); // a = b
memcpy(b, temp, size); // b = temp
}Giờ đây, hàm này hoạt động với mọi kiểu dữ liệu:
int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x giờ là 10, y giờ là 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p hiện là 2.71, q hiện là 3.14Mẫu này, sử dụng void* với size_t, là cách thư viện chuẩn của C triển khai các hàm tổng quát như qsort và bsearch.
Thử thách
DễHãy tạo một tiện ích hoán đổi tổng quát để đổi giá trị của hai biến bất kỳ, bất kể kiểu dữ liệu của chúng, bằng cách tận dụng con trỏ void* và memcpy.
Bạn sẽ sắp xếp code trong ba tệp:
swap.h: Khai báo hàmgeneric_swap, hàm này nhận hai con trỏvoid*và một tham sốsize_tcho biết kích thước dữ liệu cần hoán đổi. Đừng quên các chỉ thị bảo vệ tệp!swap.c: Cài đặt hàmgeneric_swap. Dùng bộ đệm tạm thời vàmemcpyđể di chuyển byte giữa hai vị trí bộ nhớ. Hãy nhớ quy trình hoán đổi ba bước: sao chép giá trị thứ nhất vào biến tạm, sao chép giá trị thứ hai vào vị trí thứ nhất, rồi sao chép biến tạm vào vị trí thứ hai.main.c: Minh họa cách hàm hoán đổi tổng quát hoạt động với nhiều kiểu dữ liệu. Đọc ký hiệu kiểu dữ liệu (icho số nguyên,dcho số thực double), rồi đọc hai giá trị. Tạo hai biến có kiểu dữ liệu phù hợp, hoán đổi chúng bằng hàmgeneric_swap, sau đó in cả hai giá trị sau khi hoán đổi.
Chương trình sẽ nhận ba đầu vào:
- Ký hiệu kiểu dữ liệu:
icho số nguyên hoặcdcho số thực double - Giá trị thứ nhất
- Giá trị thứ hai
Sau khi hoán đổi, hãy in mỗi giá trị trên một dòng riêng. Với số thực double, hãy dùng 2 chữ số thập phân.
Ví dụ đầu ra khi đầu vào là i, 5 và 10:
10
5Ví dụ đầu ra khi đầu vào là d, 3.14 và 2.71:
2.71
3.14Ví dụ đầu ra khi đầu vào là i, -7 và 42:
42
-7Ví dụ đầu ra khi đầu vào là d, 99.99 và 0.01:
0.01
99.99Điểm hay của cách này là hàm generic_swap không cần biết mình đang hoán đổi kiểu dữ liệu nào. Hàm chỉ di chuyển các byte. Cùng một hàm có thể dùng cho số nguyên, số thực double, struct hoặc bất kỳ kiểu dữ liệu nào khác. Bạn cần thêm <string.h> để dùng memcpy và <stddef.h> hoặc <stdlib.h> để dùng size_t.
Tự làm thử
#include <stdio.h>
#include "swap.h"
int main() {
char type;
scanf("%c", &type);
if (type == 'i') {
int a, b;
scanf("%d", &a);
scanf("%d", &b);
// TODO: Gọi generic_swap để hoán đổi a và b
// Gợi ý: Dùng &a, &b và sizeof(int)
printf("%d\n", a);
printf("%d\n", b);
} else if (type == 'd') {
double a, b;
scanf("%lf", &a);
scanf("%lf", &b);
// TODO: Gọi generic_swap để hoán đổi a và b
// Gợi ý: Dùng &a, &b và sizeof(double)
printf("%.2f\n", a);
printf("%.2f\n", b);
}
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ật7Con trỏ hàm
Khai báo con trỏ hàmGọi con trỏ hàmDùng typedef cho con trỏ hàmTruyền hàm làm đối sốÔn tập: điều phối máy tính10Bộ chứa tổng quát
Ôn tập con trỏ voidTrình bao bọc tổng quátHoán đổi tổng quátSo sánh tổng quátÔn tập: mảng tổng quá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