1.Lý do cần truyền con trỏ vào hàm

Chúng ta đã sử dụng hàm quá nhiều và đã thực hiện việc truyền các giá trị vào trong hàm như là truyền biến số, truyền mảng….

Tuy nhiên, khi thao tác với con trỏ đôi khi ta cũng cần truyền một con trỏ vào trong hàm để thực hiện các tác vụ ở trong hàm. Truyền con trỏ vào hàm thường được sử dụng trong những trường hợp sau đây:

  1. Để truyền đối số vào hàm dưới hình thức tham chiếu.
  2. Để truyền các mảng và chuỗi từ một hàm đến một hàm khác một cách thuận tiện hơn.

2.Truyền con trỏ vào trong hàm

Cú pháp khai báo hàm: (cú pháp này ví dụ cho kiểu int)

func (int *p)

Trong đó:

  • func: là tên hàm cần truyền
  • int *p: là con trỏ được truyền vào hàm có kiểu dữ liệu int (trong một số trường hợp ta có thể thay int thành bất kỳ kiểu dữ liệu nào sao cho phù hợp với bài toán)

Cú pháp sử dụng hàm func trong hàm main:

int a;
int *p = &a;
func(p);

Trong đó:

  • int a: là biến a
  • int *p = &a: là con trỏ p được gán bằng địa chỉ biến a
  • func(p): gọi và truyền con trỏ p vào hàm func

Khi con trỏ được truyền vào hàm, nó sẽ được truyền theo hình thức truyền tham chiếu nghĩa là khi có sự thay đổi giá trị con trỏ p ở trong hàm func thì cũng làm cho biến con trỏ p thay đổi ở ngoài hàm.

Để hiểu rõ hơn vấn đề trên, ta xét ví dụ khai báo một hàm có kiểu trả về là void và có tên là func nhận đối số đầu vào là một con trỏ p kiểu nguyên.

Ở trong hàm func sẽ thực hiện việc gán giá trị *p = 10 (ban đầu con trỏ p được gán bằng địa chỉ của biến a và có giá trị là 5)

void func(int *p){
    //gan gia tri *p = 10 trong ham func
    *p = 10;
}

Sau đó tôi sẽ gọi hàm func trong hàm main và kiểm tra sự thay đổi của con trỏ p ban đầu với con trỏ p sau khi được đưa vào hàm func

#include <stdio.h>
int main(){
    //tao bien a = 5
    int a = 5;
    //tao con tro p gan bang a
    int *p = &a;
    printf("Gia tri ban dau cua con tro p: %d \n", *p);
    func(p);
    printf("Gia tri sau khi con tro p truyen vao ham: %d", *p); 
}
Gia tri ban dau cua con tro p: 5

Gia tri sau khi con tro p truyen vao ham: 10

Nhận xét: Con trỏ p ban đầu được gán theo địa chỉ của biến a và có giá trị bằng 5, sau khi truyền con trỏ vào hàm func giá trị của con trỏ này đã được thay đổi bằng 10, bởi vì việc truyền con trỏ vào một hàm được coi là truyền theo hình thức tham chiếu do đó sự thay đổi của giá trị con trỏ p trong hàm func sẽ làm thay đổi con trỏ p ở ngoài hàm main. (Bạn đọc quên về tham chiếu là gì có thể xem Bài 8: Hàm và cách định nghĩa, sử dụng hàm trong C)

3.Truyền mảng, chuỗi vào hàm thông qua con trỏ

Ta hoàn toàn có thể sử dụng con trỏ để dễ dàng hơn cho việc truyền mảng hay chuỗi vào hàm, và đương nhiên mảng hay chuỗi được truyền vào hàm thông qua con trỏ đều được coi là truyền theo hình thức tham chiếu.

Cú pháp khai báo hàm: (cú pháp này ví dụ cho kiểu int)

func (int *p)

Trong đó:

  • func: là tên hàm cần truyền
  • int *p: là con trỏ được truyền vào hàm có kiểu dữ liệu int (trong một số trường hợp ta có thể thay int thành bất kỳ kiểu dữ liệu nào sao cho phù hợp với bài toán)

Cú pháp sử dụng hàm func trong hàm main:

int A[100];
func(A);

Trong đó:

  • int A[100]: là mảng a gồm 100 phần tử (có thể thay đổi kiểu dữ liệu và kích thước mảng phù hợp tùy vào yêu cầu bài toán)
  • func(A): gọi và truyền mảng A bằng con trỏ vào hàm func

Để dễ hình dung hơn ta cùng đi vào ví dụ dưới đây, tôi tạo một hàm có kiểu trả về là void, có tên là func và nhận đối số đầu vào là con trỏ số nguyên int *p

void func(int *p){
    //dung vong lap duyet cac phan tu cua a
    for(int i = 0; i < 5; i++){
        //su dung cu phap *(p + i) de lay ra cac gia tri a[i] co trong mang
        printf("A[%d] = %d \n", i, *(p + i));
    }
}

Trong hàm main tôi sẽ truyền mảng A (gồm 5 phần tử) vào hàm func theo hình thức truyền theo con trỏ:

#include <stdio.h>
int main(){
    //tao a gom 5 phan tu
    int a[5] = {1,2,3,4,5};
    //truyen a vao mang func bang con tro
    func(a);
}
A[0] = 1

A[1] = 2

A[2] = 3

A[3] = 4

A[4] = 5

Nhận xét: Mảng A được truyền vào hàm func bằng con trỏ thì khi sử dụng mảng A trong hàm func ta cần sử dụng *(p + i) để lấy giá trị của các phần tử trong A.

Chú ý: *(p+i) tương đương với a[i] nếu ta không sử dụng con trỏ.

Tiếp theo để minh họa việc truyền mảng vào hàm bằng con trỏ là truyền theo hình thức tham chiếu tôi sẽ sử dụng lại ví dụ trên, và lần này trong hàm func tôi sẽ thử thay đổi giá trị a[3] bằng cách gán *(p+ 3) = 33

void func(int *p){
    //gan gia tri *(p + 2) = 33 
    *(p + 2) = 33;
}

Kiểm tra sự thay đổi của giá trị a[3] trước và sau khi truyền vào hàm func bằng con trỏ:

#include <stdio.h>
int main(){
    //tao a gom 5 phan tu
    int a[5] = {1,2,3,4,5};
    //A[2] ban dau
    printf("A[2] ban dau: %d \n", a[2]);
    //truyen a vao func
    func(a);
    //A[2] sau khi truyen vao func
    printf("A[2] sau khi truyen vao func: %d", a[2]);
}
A[2] ban dau: 3

A[2] sau khi truyen vao func: 33

Nhận xét: Việc truyền mảng vào hàm bằng con trỏ giúp ta dễ dàng thao tác với mảng hơn với con trỏ, và việc truyền mảng này được truyền theo hình thức tham chiếu. Khi đó giá trị của mảng bị thay đổi trong hàm thì giá trị của mảng ở ngoài hàm cũng thay đổi theo.