Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình C

Truyền tham chiếu (Call by Reference) trong C qua con trỏ

Khái niệm

C không có truyền tham chiếu gốc như C++ (void f(int &x)). Thay vào đó, truyền địa chỉ biến (con trỏ) để hàm có thể truy cập và sửa đổi biến gốc.

Cơ chế

void ham(int *x) {  // Nhận địa chỉ
    *x = 20;        // Sửa giá trị tại địa chỉ
}

int main() {
    int a = 10;
    ham(&a);        // Truyền địa chỉ của a
    // a giờ là 20
}

Chương trình: Hoán đổi 2 số (THÀNH CÔNG)

#include <stdio.h>

void hoanDoi(int *a, int *b) {
    int tam = *a;
    *a = *b;
    *b = tam;
}

int main() {
    int x = 10, y = 20;
    
    printf("Truoc hoan doi: x=%d, y=%d\n", x, y);
    hoanDoi(&x, &y);  // Truyền địa chỉ
    printf("Sau hoan doi: x=%d, y=%d\n", x, y);
    
    return 0;
}

Kết quả

Truoc hoan doi: x=10, y=20
Sau hoan doi: x=20, y=10  // THÀNH CÔNG!

Sơ đồ bộ nhớ

main():           hoanDoi():
┌─────────┐       ┌──────────────┐
│ x = 10  │◄──────│ *a (trỏ đến x)│
│ (0x100) │       │ a = 0x100    │
├─────────┤       ├──────────────┤
│ y = 20  │◄──────│ *b (trỏ đến y)│
│ (0x104) │       │ b = 0x104    │
└─────────┘       └──────────────┘
  Cùng địa chỉ - cùng ô nhớ

Các trường hợp bắt buộc dùng Call by Reference

1. Hàm cần sửa đổi biến gốc

void tangLen(int *x) { (*x)++; }
void nhapSo(int *x) { scanf("%d", x); }  // scanf cần địa chỉ

2. Truyền struct/mảng lớn (tránh copy tốn kém)

struct SinhVien { char ten[50]; int diem[10]; };

// Sai - copy cả struct lớn
void inSV(struct SinhVien sv) { ... }

// Đúng - truyền con trỏ
void inSV(const struct SinhVien *sv) { ... }

3. Hàm cần trả về nhiều giá trị

void tachSo(int n, int *hangChuc, int *hangDonVi) {
    *hangChuc = n / 10;
    *hangDonVi = n % 10;
}

int main() {
    int a, b;
    tachSo(45, &a, &b);  // a=4, b=5
}

4. Cấp phát bộ nhớ động trong hàm

int taoMang(int **arr, int n) {
    *arr = malloc(n * sizeof(int));
    return *arr != NULL;
}

int main() {
    int *a;
    if (taoMang(&a, 10)) { ... }
    free(a);
}

Const correctness với con trỏ

Khai báo Ý nghĩa
void f(int *p)Có thể sửa *p, có thể đổi p
void f(const int *p)KHÔNG được sửa *p (read-only)
void f(int *const p)KHÔNG được đổi p (con trỏ const)
void f(const int *const p)Cả hai đều không được sửa

Best Practice

// 1. Dùng const nếu không sửa đổi
void inMang(const int *arr, int n) { ... }

// 2. Kiểm tra NULL
void anToan(int *p) {
    if (!p) return;  // Hoặc assert(p)
    *p = 10;
}

// 3. Restrict (C99) - compiler optimize tốt hơn
void copy(int *restrict dest, const int *restrict src, int n);

Tóm tắt

  • Call by Value: Truyền bản sao, an toàn, mặc định cho kiểu cơ bản
  • Call by Reference (C style): Truyền &bien, hàm nhận *con_tro, có thể sửa gốc
  • Mảng: Luôn truyền như con trỏ (địa chỉ phần tử 0)
  • Luôn dùng const cho tham số chỉ đọc