THAM CHIẾU VÀ THAM TRỊ


Một trong những vấn đề các bạn mới học C/C++ hay gặp lỗi là truyền tham số khi gọi hàm, trong bài này mình sẽ giải thích kỹ để các bạn có thể hiểu rõ.

Trước khi bắt đầu các bạn nên đọc qua 2 bài viết về con trỏ (link required) và bộ nhớ stack (link required).

Như đã biết, khi gọi hàm, tham số sẽ được tạo ra trên stack và được giải phóng NGAY KHI kết thúc. Có nghĩa là các giá trị của nó sẽ bị hủy. Giả sử bạn có một hàm được khai báo là void f(int n), khi bạn gọi f(m), trước khi thực thi, tham số n sẽ được tạo ra trên stack, giá trị của m sẽ được sao chép vào n. Bên trong f sẽ không có bất kỳ liên hệ nào với m, nên khi kết thúc hàm trở về, ta sẽ thấy m vẫn còn mang giá trị cũ, vì những gì thay đổi trên n là thay đổi biến n trong stack.Trong C chỉ có 1 cách truyền đó nên khi muốn giữ lại giá trị của tham số, ta phải quy ước là truyền con trỏ cho hàm, và bên trong hàm đó bạn sẽ thao tác trên vùng nhớ mà con trỏ đó trỏ đến.Như vậy khi ta khai báo fx(int *n), rồi gọi fx(&m) thì một biến con trỏ sẽ được tạo ra trên stack, giá trị của nó sẽ được sao chép từ địa chỉ của m. Bên trong fx, khi nói đến n nghĩa là đang nói đến địa chỉ của m, do vậy *n chính là biến m, thay đổi giá trị của *n cũng chính là thay đổi m.Trong C chỉ có một cách duy nhất là truyền theo tham trị, truyền theo con trỏ cũng chỉ là truyền theo tham trị, chỉ khác là bạn dùng biến có kiểu con trỏ mà thôi, bên trong hàm bạn vẫn phải xử lý biến đó theo cách sử dụng con trỏ.Hãy đảm bảo bạn đã hiểu toàn bộ phần trên trước khi đọc phần tiếp theo, vì phần trên mới là phần quan trọng nhất.

TRUYỀN THEO THAM CHIẾU

Vì cách truyền qua con trỏ sẽ cần bạn phải để ý một chút khi thao tác, chưa kể cú pháp đôi khi hơi dài dòng nên trong C++ người ta đưa ra thêm một cách truyền mới là truyền theo tham chiếu.Nói một cách dễ hiểu nhất, truyền theo tham chiếu chính là truyền theo con trỏ, nhưng trình biên dịch sẽ xử lý thay cho bạn các thao tác trên con trỏ đó, điều này giúp mọi thứ đơn giản hơn, từ đó cũng giảm sai sót khi lập trình.Nếu bạn có một function int fx(int &n), khi bạn gọi fx(m), trình biên dịch sẽ diễn giải thành int fx(int *n) và fx(&m), thao tác bên trong hàm n = 5 sẽ được chuyển thành *n = 5.Tới đây bạn đã biết tại sao cần hiểu phần phía trên trước khi đọc phần sau chưa? Vì phần sau quá đơn giản, hầu như chẳng có gì cần phải suy nghĩ.

Ghi chú:

– Các trình dịch hiện tại hầu hết đều là trình dịch C++, do vậy bạn luôn có thể dùng tham chiếu.

– Trong các ngôn ngữ Java và C#, chỉ có một cách duy nhất là truyền qua tham trị. Tuy nhiên các kiểu dữ liệu trong 2 ngôn ngữ này lại chia làm 2 loại: value type (int, bool, char, double, enum…) và reference type (class, nôm na là những kiểu mà bạn phải gọi new trước khi sử dụng). Do vậy cần lưu ý một chút với các kiểu reference: nếu bạn có một method int fx(MyClass c), khi gọi fx(myclass) thì bên trong phương thức đó, nếu bạn viết c = cx sẽ làm thay đổi cả biến c (nhưng không ảnh hưởng tới myclass. Nhưng nếu viết c.x = 10 sẽ làm thay đổi myclass.x, bởi bản thân c là một tham chiếu.

– Ở đây có hình ảnh minh họa khá hay: https://blog.penjee.com/passing-by-value-vs-by-reference-java-graphical/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: