Khi nào nên dùng tham chiếu, tham trị, biến toàn cục?
Như tiêu đề thì hôm nay mình chia sẻ về việc dùng tham chiếu, tham trị, biến toàn cục, để các bạn hiểu rõ hơn và không phải băn khoan về cách dùng cũng như khi nào dùng nó nữa! Về lý thuyết thì mình không nói nữa, các bạn có thể tham khảo trên các diễn đàn, blogs khác. 1. Tổng ...
Như tiêu đề thì hôm nay mình chia sẻ về việc dùng tham chiếu, tham trị, biến toàn cục, để các bạn hiểu rõ hơn và không phải băn khoan về cách dùng cũng như khi nào dùng nó nữa!
Về lý thuyết thì mình không nói nữa, các bạn có thể tham khảo trên các diễn đàn, blogs khác.
1. Tổng quan về tham trị và tham chiếu
a. Tham trị
Khi truyền tham trị, trình biên dịch sẽ tạo 1 bản sao và truyền bản sao đó vào nơi muốn truyền, do đó mọi thay đổi sẽ xảy ra trên bản sao này và không ảnh hưởng đến biến gốc.
b. Tham chiếu
Còn truyền tham chiếu thì nó sẽ truyền ngay địa chỉ của biến hay gọi là truyền bản chính đó vào nơi cần dùng, nên ở trong hàm mà mình truyền vào có thay đổi gì thì biến ngoài (biến bản gốc) cũng thay đổi theo.
Ví dụ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> #include <conio.h> void thamtri(int a,int b) { a = a + 123; b = b + 987; } void thamchieu(int &a,int &b) { a = a + 10; b = b + 5; } int main() { int a=1,b=2; thamtri(a,b); printf("a = %d , b = %d
",a,b); thamchieu(a,b); printf("a = %d, b = %d
",a,b); } |
Ví dụ trên sẽ ra kết quả như sau:
c. Khác biệt về cú pháp
Khác nhau dễ thấy nhất là phần khai báo hàm:
Tham trị: void thamtri(int a, int b);
Tham chiếu: void thambien(int &a, int &b);
Tham chiếu có dấu & còn tham trị không có dấu &
Dễ nhìn thấy như mô tả ở trên về tham trị, ở trong hàm thamtri mình có tăng a lên 123, b lên 987, nhưng khi ra khỏi hàm thì in ra màn hình 2 biến đó, kết quả là 1 và 2. chứng tỏ khi truyền tham trị ở bên trong hàm được truyền, có thay đổi gì thì ở ngoài, sử dụng theo kết quả của biến gốc.
Tương tự, ở hàm thamchieu, mình tăng a lên 10, b lên 5, và ra khỏi hàm thì a và b sẽ lần lượt là 11 và 7.
cũng trong ví dụ trên nếu mình in giá trị a và b ngay trong hàm thì kết quả sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #include <stdio.h> #include <conio.h> void thamtri(int a,int b) { a = a + 123; b = b + 987; printf("Gia tri khi trong ham tham tri: a = %d , b = %d
",a,b); } void thamchieu(int &a,int &b) { a = a + 10; b = b + 5; printf("Gia tri khi trong ham tham chieu: a = %d, b = %d
",a,b); } int main() { int a=1,b=2; thamtri(a,b); printf("a = %d , b = %d
",a,b); thamchieu(a,b); printf("a = %d, b = %d
",a,b); } |
2. Khi nào nên dùng tham chiếu và tham trị?
Để trả lời câu hỏi này, bạn phải biết lợi ít của việc dùng hàm là gì? Mình xin chia sẻ như sau.
a. Lợi ích của việc dùng hàm và liên quan đến tham chiếu tham trị
– Lợi ích việc dùng hàm là giúp bạn chia nhỏ chương trình ra, dễ dàng kiểm soát lỗi, dễ viết chương trình, nhìn vào cả chương trình sẽ mạch lạc hơn.
– việc dùng hàm sẽ giúp bạn rút ngắn thời gian hơn cho việc xử lí các yêu cầu tương tự. Ví dụ đề yêu cầu bạn viết chương trình nhập mảng A, B. Bạn chỉ cần viết 1 lần và dùng cho cả 2.
– Có thể còn nhiều lí do nữa, nhưng mình chưa nghĩ ra.
Ở đây mình nhấn mạnh đến lợi ích thứ 2, mình nghĩ nó ít nhiều cũng có liên quan đến tham chiếu.
Lấy ví dụ mình nói ở trên, Đề bài yêu cầu bạn “viết chương trình nhập mảng A, B” một số bạn sẽ viết như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdio.h> #include <iostream> using namespace std; int main() { int a[100], b[100], n, m; cin >> n; for (int i=0; i<n; i++) cin >> a[i]; cin >> m; for (int i=0; i<m; i++) cin >> b[i]; } |
Một số khác lại dùng hàm nhưng lại sử dụng như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> #include <iostream> using namespace std; void nhapa(int a[100], int &n) { cin >> n; for (int i=0; i<n; i++) cin >> a[i]; } void nhapb(int b[100], int &m) { cin >> m; for (int i=0; i<m; i++) cin >> b[i]; } int main() { int a[100], b[100], n, m; nhapa(a,n); nhapb(b,m); } |
Nhìn chung 2 cách trên đều đáp ứng yêu cầu đề bài. Tuy nhiên đánh giá chung là chưa khai thác hết lợi ít của nó. Ở cách thứ 2 người dùng sử dụng hàm để đạt được lợi ích thứ nhất là dễ quản lí, tuy nhiên chưa đạt được lợi ích 2 là dùng nhiều lần. Vì ở ví dụ trên thật chất việc nhập mảng a và mảng b đều có các thông số đầu vào tương tự nhau. nên mình sẽ ứng dụng thử trong đoạn code dưới đây giúp các bạn hình dung:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <stdio.h> #include <iostream> using namespace std; void nhap(int a[100], int &n) { cin >> n; for (int i=0; i<n; i++) cin >> a[i]; } int main() { int a[100], b[100], n, m; nhap(a,n); nhap(b,m); } |
ở trên mình truyền tham chiếu số phần thử, và 1 cái mảng, à, ngay mảng nhiều bạn sẽ hỏi vì sao ko có dấu & mình cũng chưa rõ lắm về vấn đề này, tuy nhiên dường như nó có liên quan đến pointer, nên nói chung truyền mảng thì mặc định các bạn hiểu là nó truyền theo nguyên tắc của tham chiếu đi. còn biến thông thường thì không, nên mình phải int &n .
Như vậy thì đoạn code trên được dùng 2 lần cho việc lưu giá trị vào mảng.
b. Bài tập minh họa
Nhập từ bàn phím 4 số nguyên, a, b, c, d hãy hoán đổi vị trí của a với b, và c với d.
ví dụ a=1; b=2; c=3; d=4;
-> kết quả sau khi hoán đổi phải là: a=2; b=1; c=4; d=3;
Thay vì mình viết hẳn 2 đoạn chương trình để thực hiện việc đó, ví dụ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <stdio.h> #include <iostream> using namespace std; int main() { int a,b,c,d, tam; cin >> a>> b>> c>> d; tam = a; a = b; b = tam; tam = c; c = d; d = tam; cout << a << " "<< b << " "<< c << " " << d; } |
Thì mình có thể dùng hàm, và truyền tham chiếu để xử lí nó như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <stdio.h> #include <iostream> using namespace std; void hoanvi(int &a, int &b) { int tam; tam = a; a = b; b = tam; } int main() { int a,b,c,d, tam; cin >> a>> b>> c>> d; hoanvi(a,b); hoanvi(c,d); cout << a << " "<< b << " "<< c << " " << d; } |
Các bạn lưu ý phải truyền tham chiếu thì ra khỏi hàm biến mới giữ giá trị đã xử lí bên trong hàm nhé.
Mình cũng lưu ý thêm, khi bạn viết các chương trình lớn, những phần code tương tự nhau, vd như bài trên: mà các bạn không biết dùng đến hàm và truyền tham số thì sẽ dễ dẫn đến tình trạng: sau khi giải quyết được yêu cầu hoán đổi a với b, các bạn copy đoạn code đó đem xuống chỉnh lại tên biến cho c với d.
Việc này dễ sai, vì sao?
– Trong quá trình sao chép, lỡ như bạn gõ nhầm gì đó thì hóa ra lại sai.
– Hơn nữa, giả sử sau khi làm xong bài, bạn phát hiện “Ahhhh, phần hoán đổi a, b mình làm sai” -> “Omggg, phần hoán đổi c với d, mình copy theo cái đoạn xử lí a và b nữa??” -> thế nên dễ dẫn đến tình trạng sửa lỗi a và b, xong xuống dưới phải sửa lại c và d. Nhiều lần như vậy sẽ gây tốn rất nhiều thời gian. và thiếu tính đúng đắn ở kết quả.
Ok, truyền tham số phiền quá!! Mình muốn sử dụng lợi ích đầu tiên – dễ quản lí chương trình hơn thì phải làm sao?
– Đơn giản nhất là bạn khai báo biến toàn cục thôi và không cần truyền thôi. (biến toàn cục là biến dùng được cho tất cả các hàm, và tất nhiên nếu trong hàm có biến cùng tên thì máy sẽ sử dụng biến trong hàm, còn nếu trong hàm không có biến đó thì nó sẽ dùng biến ở ngoài (biến toàn cục)).
– Tuy nhiên nếu vậy thì bạn sẽ khó khai thác được lợi ích thứ 2 là dùng nhiều lần.
Dưới đây là ví dụ cho các bài mình nêu ở trên:
Nhập vào mảng a, b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include <stdio.h> #include <iostream> using namespace std; int a[100], b[100], n, m; void nhapa() { cin >> n; for (int i=0; i<n; i++) cin >> a[i]; } void nhapb() { cin >> m; for (int i=0; i<m; i++) cin >> b[i]; } int main() { nhapa(); nhapb(); } |
Hoán đổi vị trí a, b , c , d
- 1 PTIT013C spoj PTIT – Khoảng cách
- 2 BCTHIDAU spoj PTIT – Thi đấu
- 3 C11PRIME spoj – Số nguyên tố
- 4 MORSEDEC spoj – Morse decoding
- 5 PTIT135G spoj PTIT – Blackjack
- 6 Đề thi Olympic 30/4 môn tin học khối 11 năm 2015
- 7 PTIT124J spoj – chuyển nhị phân sang bát phân
- 8 ADS spoj – Quảng cáo
- 9 Đề thi Olympic 30/4 môn tin học khối 10 năm 2015
- 10 [Bài tập] Giáo trình bài tập kỹ thuật lập trình