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 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ụ

#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 \n",a,b);

    thamchieu(a,b);
    printf("a = %d, b = %d \n",a,b);
}

Ví dụ trên sẽ ra kết quả như sau:

vd1

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:

#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 \n",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 \n",a,b);
}

int main()
{
    int a=1,b=2;

    thamtri(a,b);
    printf("a = %d , b = %d \n",a,b);

    thamchieu(a,b);
    printf("a = %d, b = %d \n",a,b);
}

vd2

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:

#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:

#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:

#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:

#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:

#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

#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

#include <stdio.h>
#include <iostream>
using namespace std;

int a,b,c,d;

void hoanvi1()
{
    int tam;
    tam = a;
    a = b;
    b = tam;
}

void hoanvi2()
{
    int tam;
    tam = c;
    c = d;
    d = tam;
}

int main()
{

    cin >> a>> b>> c>> d;
    hoanvi1();
    hoanvi2();
    cout << a << " "<< b << " "<< c << " " << d;
}

Nếu bạn hỏi mình : “Bạn hay dùng hàm, các biến, pla pla loại nào?” thì mình xin trả lời rằng:

– Mảng, biến kết quả, nói dung dữ liệu input, mình khai báo toàn cục.

– Biến chạy i, j, biến tạm (biến rác) mình khai báo cục bộ (ở trong hàm).

– Nhập dữ liệu mình ưu tiên lợi ích một và tất nhiên có 1 hàm riêng để thực hiện công việc nhập, nhưng mình sẽ không truyền gì nhé. ví dụ như void nhap()

……… Trước mắt chỉ có vậy thôi 😀 bạn hỏi mình vì sao thì tự tìm hiểu lí do nha :))

3. Khi nào nên dùng tham trị?

Như đã giới thiệu ở trên thì loại truyền này không làm thay đổi giá trị khi ra khỏi hàm. nên nó thường dùng ở các trường hợp như: cần xuất 1 mảng, cần kiểm tra số n có là nguyên tố không? (vì bạn đưa vào số n, bạn yêu cầu chương trình cho bạn biết YES hay NO, chứ không tác động đến giá trị n, nên lúc này chỉ dùng tham trị là đủ, đại loại là vậy),… và nhiều trường hợp khác.

Hi vọng những phần mình nói ở trên sẽ giúp các bạn hiểu hơn về lí thuyết và cách dùng của tham chiếu, tham trị….

Nếu có bất kì thắc mắc nào, các bạn vui lòng đặt câu hỏi dưới đây, mình sẽ giải đáp và để mọi người cùng hiểu hơn.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *