Menu
CoddyTech

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à 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// p hiện là 2.71, q hiện là 3.14

Mẫ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.

challenge icon

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àm generic_swap, hàm này nhận hai con trỏ void* và một tham số size_t cho 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àm generic_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 (i cho số nguyên, d cho 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àm generic_swap, sau đó in cả hai giá trị sau khi hoán đổi.

Chương trình sẽ nhận ba đầu vào:

  1. Ký hiệu kiểu dữ liệu: i cho số nguyên hoặc d cho số thực double
  2. Giá trị thứ nhất
  3. 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
5

Ví dụ đầu ra khi đầu vào là d, 3.14 và 2.71:

2.71
3.14

Ví dụ đầu ra khi đầu vào là i, -7 và 42:

42
-7

Ví 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;
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch C online