Tham chiếu trong lập trình là gì

Trong bài viết này chúng ta sẽ cùng tìm hiểu về 3 cách truyền tham số cho hàm trong lập trình C/C++, với những nội dung sau:

  1. Tham số truyền bằng Tham trị [dùng trong cả C và C++]
  2. Tham số truyền bằng Tham chiếu [CHỈ DÙNG trong C++]
  3. Tham số truyền bằng Con trỏ [dùng trong cả C và C++]

Hàm trong C/C++ hoạt động theo nguyên tắc:
Khi gọi hàm, 1 bản sao của tham số được tạo ra, sau đó cấp phát vùng nhớ mới, copy giá trị sang [quá trình này được gọi là shadow copy], và hàm sẽ làm việc với bản sao này. [Trong C++ nó sẽ dùng hàm tạo sao chép để tiến hành quá trình shadow copy này]

Có thể bạn quan tâm: Cách cấp phát và giải phóng bộ nhớ trong lập trình C.

1. Tham số truyền bằng Tham trị [dùng trong cả C và C++]
1. Tham số truyền bằng Tham trị [dùng trong cả C và C++]

Khi tham số của hàm được truyền bằng tham trị tức là truyền giá trị cho hàm, khi đi vào hàm sẽ tạo ra 1 vùng nhớ mới và mọi thao tác tính toán chỉ thao tác trên vùng nhớ mới đó, còn vùng nhớ cũ vẫn không thay đổi. Cho nên sau khi thực hiện xong, ra khỏi hàm thì giá trị của biến được truyền vào làm tham số sẽ không thay đổi, do không có ảnh hưởng trực tiếp đến vùng nhớ cũ.

=> Tham trị sẽ không làm thay đổi giá trị sau khi kết thúc lời gọi hàm. Do đó, tham trị được sử dụng khi không có nhu cầu thay đổi giá trị của tham số truyền vào.

#include // Truyền tham trị void Xuly[int a] { a = a + 10; printf["Dia chi cua bien a trong ham Xuly: %p\n", &a]; printf["Gia tri cua bien a trong ham Xuly: %d\n", a]; } int main[] { int a = 5; Xuly[ a ]; printf["\nDia chi cua bien a trong ham main: %p\n", &a]; printf["Gia tri cua bien a trong ham main: %d\n", a]; getchar[]; return 0; }

Kết quả chương trình

Từ kết quả chạy chương trình các bạn có thể thấy, địa chỉ của biến a trong hàm XuLy[012FF64C] và trong hàm main[012FF720] là khác nhau nên việc tính toán bên trong hàm XuLy sẽ không làm ảnh hưởng đến giá trị a ở hàm main.

2. Tham số truyền bằng Tham chiếu [CHỈ DÙNG trong C++]
2. Tham số truyền bằng Tham chiếu [CHỈ DÙNG trong C++]

Khi tham số của hàm được truyền bằng tham chiếu tức là truyền địa chỉ cho hàm, do đó khi đi vào hàm mọi thao tác tính toán sẽ được thực hiện trên vùng nhớ cũ, cho nên sau khi ra khỏi hàm giá trị đã bị thay đổi.

=> Tham chiếu sẽ làm thay đổi giá trị sau khi kết thúc lời gọi hàm. Do đó, tham chiếu được sử dụng khi có nhu cầu thay đổi giá trị của tham số truyền vào.

#include // Truyền tham chiếu void Xuly[int &a] { a = a + 10; printf["Dia chi cua bien a trong ham Xuly: %p\n", &a]; printf["Gia tri cua bien a trong ham Xuly: %d\n", a]; } int main[] { int a = 5; Xuly[ a ]; printf["\nDia chi cua bien a trong ham main: %p\n", &a]; printf["Gia tri cua bien a trong ham main: %d\n", a]; system[ "pause" ]; return 0; }

Kết quả chương trình

Từ kết quả chạy chương trình các bạn có thể thấy, địa chỉ của biến a trong hàm XuLy và hàm main là giống nhau[00AFFA20] nên việc tính toán bên trong hàm XuLy thì sẽ ảnh hưởng trực tiếp và làm thay đổi giá trị của biến a ở hàm main [cả 2 đều bằng 15].

3. Tham số truyền bằng Con trỏ [dùng trong cả C và C++]
3. Tham số truyền bằng Con trỏ [dùng trong cả C và C++]

Có thể bạn quan tâm: Cách sử dụng con trỏ trong lập trình C/C++.

#include // Truyền con trỏ // *n: toán tử * ở đây biểu thị n là 1 BIẾN CON TRỎ void Xuly[int *n] { // Bên trong hàm: // *n: toán tử * dùng để lấy GIÁ TRỊ của biến con trỏ n *n = *n + 10; } int main[] { /* * Trường hợp biến thường */ int a = 5; // Vì tham số của hàm XuLy là 1 con trỏ, bản chất con trỏ là địa chỉ // Nên tham số truyền vào hàm XuLy cũng fai là 1 địa chỉ: &a Xuly[ &a ]; printf["Gia tri cua a = %d\n", a]; /* * Trường hợp biến con trỏ */ // Cấp phát vùng nhớ cho con trỏ int *b = [int *]malloc[ sizeof[int *] ]; // Khởi tạo giá trị cho con trỏ *b = 5; // 2 dòng cấp phát và khởi tạo thì TƯƠNG ĐƯƠNG với 1 dòng trong C++ là: // int *b = new int[5]; // b đã là 1 con trỏ rồi, nên khi truyền tham số không cần dấu & nữa. Xuly[ b ]; printf["\nGia tri cua b = %d", *b]; // Giải phóng vùng nhớ đã cấp phát free[b]; getchar[]; return 0; }

Kết quả chương trình

Một số lưu ý ở phần này:
Đối với tham số được truyền bằng con trỏ thì hàm vẫn cứ làm theo nguyên tắc đã nêu trên và 1 bản sao của con trỏ được tạo ra, và hàm làm việc với bản sao này, và trước khi gọi hàm con trỏ trỏ vào đâu thì nó vẫn được trỏ vào đấy.

#include #include #include // Tham số truyền bằng con trỏ void XuLy[int *a] { *a = 2; a++; } int main[] { // Khai báo và cấp phát vùng nhớ cho con trỏ int *a = [int *]calloc[ 1, sizeof[int *] ]; printf["Dia chi Truoc : %x\n", a]; // trước và sau khi gọi hàm XuLy[ a ]; // con trỏ a trỏ vào đâu printf["Dia chi Sau : %x\n", a]; // thì nó vẫn trỏ vào đó // Giải phóng vùng nhớ đã cấp phát free[ a ]; getchar[]; return 0; }

Địa chỉ của con trỏ Trước và Sau thực hiện hàm là Giống nhau: c70fd8

Vậy con trỏ ko thay đổi thì cái gì thay đổi được?
Đó chính là giá trị nằm trong vùng nhớ trỏ đến thay đổi. Do biến a của ta nằm trong vùng nhớ được trỏ đến nên nó được thay đổi.

・ Ví dụ 1:

#include #include // Tham số truyền bằng con trỏ void XuLy[int *a] { *a = 2; // làm việc với địa chỉ nhận được } int main[] { int a; XuLy[ &a ]; // truyền địa chỉ của a vào cho hàm printf["a = %d", a]; // do đó sau hàm này a = 2 getchar[]; return 0; }

・ Ví dụ 2:

#include #include #include // Tham số truyền bằng con trỏ void XuLy[int *a] { *a = 2; // làm việc với địa chỉ nhận được } int main[] { // Khai báo và cấp phát vùng nhớ cho con trỏ int *a = [int *]calloc[1, sizeof[int *]]; printf["Truoc a = %d\n", *a]; XuLy[ a ]; // truyền địa chỉ của a vào cho hàm printf["Sau a = %d\n", *a]; // do đó sau hàm này a = 2 // Giải phóng vùng nhớ đã cấp phát free[a]; getchar[]; return 0; }

Giá trị của biến a được thay đổi

Tránh thao tác sai như sau:
Ban đầu con trỏ chưa trỏ đến đâu cả và được truyền vào hàm. Trong hàm chúng ta cấp phát bộ nhớ rồi cho bản sao đang làm việc trỏ đến. Sau đó ra khỏi hàm rồi thì con trỏ a của ta vẫn chưa có trỏ vào bộ nhớ nào cả, nên khi truy xuất vào giá trị của con trỏ a sẽ bị lỗi.

#include #include void XuLy[ int *a ] { // Cấp phát bộ nhớ cho con trỏ bên trong hàm được gọi // nên sẽ không liên quan gì đến vùng nhớ của con trỏ a ở hàm main a = [ int * ]calloc[ 1, sizeof[int] ]; *a = 2; printf[ "a = %d\n", *a ]; // Output: a = 2 } int main[] { int *a = NULL; XuLy[ a ]; // Kiểm tra con trỏ có NULL hay không if [ a ] { printf[ "Not NULL" ]; } else { printf[ "NULL" ]; // Output: NULL } printf[ "a = %d\n", *a ]; // Xảy ra lỗi run-time, // ném ra 1 exception vì con trỏ a là con trỏ null getchar[]; return 0; }

Vì truyền tham chiếu hay truyền con trỏ cho hàm đều làm thay đổi giá trị của biến tham số sau khi ra khỏi hàm. Do đó chúng ta có thể ứng dụng khi muốn lấy nhiều kết quả trả về từ 1 hàm. Ví dụ:

#include // Truyền tham chiếu void TinhToan_1[int a, int b, int &Tong, int &Hieu, int &Tich] { Tong = a + b; Hieu = a - b; Tich = a * b; } // Truyền con trỏ // *Tong, *Hieu, *Tich: toán tử * ở đây biểu thị đây là 3 CON TRỎ Tong, Hieu Tich void TinhToan_2[int a, int b, int *Tong, int *Hieu, int *Tich] { // Bên trong hàm: // *Tong, *Hieu, *Tich: toán tử * dùng để lấy 3 GIÁ TRỊ của con trỏ Tong, Hieu Tich *Tong = a + b; *Hieu = a - b; *Tich = a * b; } int main[] { int a = 9; int b = 3; int Tong1, Hieu1, Tich2; int Tong2, Hieu2, Tich2; // Gọi hàm tính toán TinhToan_1[ a, b, Tong1, Hieu1, Tich2 ]; TinhToan_2[ a, b, &Tong2, &Hieu2, &Tich2 ]; // Lấy được 3 giá trị kết quả tổng, hiệu, tích printf["Tong cua hai so %d va %d la: %d, %d\n", a, b, Tong1, Tong2]; printf["Hieu cua hai so %d va %d la: %d, %d\n", a, b, Hieu1, Hieu2]; printf["Tich cua hai so %d va %d la: %d, %d\n", a, b, Tich2, Tich2]; system[ "pause" ]; return 0; }

Kết quả chương trình

Cảm ơn bạn đã theo dõi. Đừng ngần ngại hãy cùng thảo luận với chúng tôi!
4.4 7 votes
Đánh giá bài viết

Video liên quan

Chủ Đề