C++ FUNCTIONS – CÁC HÀM TRONG C++

By 08/01/2021Tháng Sáu 10th, 2021C/C++, KIẾN THỨC LẬP TRÌNH

Các hàm trong C++

Hàm là một code block thực hiện một nhiệm vụ cụ thể.

Giả sử chúng ta cần viết một chương trình để tạo một hình tròn và tô màu cho nó. Chúng ta có thể dùng hàm để giải quyết vấn đề này:

  • Một hàm để vẽ vòng tròn
  • Một hàm để tô màu cho vòng tròn

Chia một vấn đề phức tạp thành các phần nhỏ hơn giúp chương trình của chúng ta dễ hiểu hơn và có thể sử dụng lại.

Có 2 loại hàm:

  • Thư viện hàm chuẩn: có sẵn trong C++
  • Hàm do người dùng định nghĩa: do người dùng tạo

Trong nội dung của bài, tôi sẽ tập trung chủ yếu vào các hàm do người dùng định nghĩa.

C++ cho phép lập trình viên xác định hàm của riêng họ.

Một hàm do người dùng định nghĩa sẽ nhóm code lại để thực hiện một nhiệm vụ cụ thể và nhóm code đó sẽ được đặt tên (định danh).

Khi hàm được gọi từ bắt kỳ phần nào của chương trình, tất cả sẽ thực thi các mã được xác định trong phần thân của hàm.

1.1. Khai báo hàm trong C++

Cú pháp khai báo hàm là:

returnType functionName (parameter1, parameter2,…) {

    // function body   

}

Đây là một ví dụ về khai báo hàm:

// function declaration

void greet() {

    cout << “Hello World”;

}

Tại đây:

  • Tên của hàm là greet()
  • Kiểu trả về của hàm là void
  • Dấu ngoặc đơn trống có nghĩa là nó không có bất kỳ tham số nào
  • Phần thân hàm được viết bên trong { }

Lưu ý: chúng ta sẽ tìm hiểu về kiểu trả về và các tham số ở phần sau của bài.

1.2. Gọi hàm

Trong chương trình trên, tôi đã khai báo một hàm có tên là greet(). Để sử dụng hàm greet(), tôi cần gọi nó.

Đây là cách để tôi gọi hàm greet() bên trên:

int main() {

     

    // calling a function   

    greet(); 

 

}

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-call.png

Ví dụ 1: Hiển thị văn bản

#include <iostream>

using namespace std;

 

// declaring a function

void greet() {

    cout << “Hello there!”;

}

 

int main() {

 

    // calling the function

    greet();

 

    return 0;

}

Đầu ra

Hello there!

1.3. Tham số của hàm

Như đã đề cập ở trên, một hàm có thể được khai báo với các tham số (đối số). Tham số là một giá trị được truyền khi khai báo một hàm.

Chúng ta hãy xem xét hàm ở ví dụ dưới dây:

void printNum(int num) {

    cout << num;

}

Ở đây biến int num là tham số của hàm.

Tôi truyền một giá trị cho tham số của hàm trong khi gọi hàm

int main() {

    int n = 7;

    

    // calling the function

    // n is passed to the function as argument

    printNum(n);

    

    return 0;

}

Ví dụ 2: Hàm với tham số

// program to print a text

 

#include <iostream>

using namespace std;

 

// display a number

void displayNum(int n1, float n2) {

    cout << “The int number is “ << n1;

    cout << “The double number is “ << n2;

}

 

int main() {

     

     int num1 = 5;

     double num2 = 5.5;

 

    // calling the function

    displayNum(num1, num2);

 

    return 0;

}

Đầu ra

The int number is 5

The double number is 5.5

Ở chương trình trên, tôi sử dụng hàm có một tham số int và một tham số double.

Sau đó, tôi chuyển num1 và num2 làm đối số. Các giá trị này được lưu trữ bởi các tham số của hàm là n1 và n2 tương ứng.

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-parameters.png

Lưu ý: Kiểu của các đối số được truyền trong khi gọi hàm phải khớp với các tham số tương ứng được xác định trong khai báo hàm.

1.4. Câu lệnh trả về

Trong chương trình trên, tôi đã dùng void trong khai báo hàm. Ví dụ:

void displayNumber() {

    // code

}

Điều này có nghĩa là hàm không trả về bất kỳ giá trị nào

Nó cũng có thể trả về một giá trị từ một hàm khi ta chỉ định kiểu trả về của hàm trong khi khai báo hàm.

Ví dụ:

int add (int a, int b) {

   return (a + b);

}

Ở đây, tôi có kiểu dữ liệu int thay vì void. Nghĩa là hàm trả về một giá trị int.

Đoạn mã trả về (a+b), trả về tổng của 2 tham số dưới dạng giá trị hàm.

Câu lệnh trả về thể hiện rằng hàm đã kết thúc. Bất kỳ mã nào sau khi trả về bên trong hàm sẽ không được thực thi.

Ví dụ 3: Thêm 2 số

// program to add two numbers using a function

 

#include <iostream>

 

using namespace std;

 

// declaring a function

int add(int a, int b) {

    return (a + b);

}

 

int main() {

 

    int sum;

    

    // calling the function and storing

    // the returned value in sum

    sum = add(100, 78);

 

    cout << “100 + 78 = “ << sum << endl;

 

    return 0;

}

Đầu ra

100 + 78 = 178

Trong chương trình trên, hàm add() được dùng để tìm tổng của 2 số.

Tôi truyền 2 ký tự int 100 và 78 trong khi gọi hàm.

Tôi cũng lưu trữ giá trị trả về của hàm trong biến tổng sau đó xuất ra.

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-return-statement.png

Lưu ý rằng sum là một biến kiểu int, do giá trị trả về của add() là kiểu int.

1.5. Hàm nguyên mẫu

Trong C++, mã khai báo hàm phải ở trước lệnh gọi hàm. Tuy nhiên, nếu chúng ta muốn xác định một hàm sau lời gọi hàm, chúng ta cần sử dụng hàm nguyên. Ví dụ:

// function prototype

void add(int, int);

 

int main() {

    // calling the function before declaration.

    add(5, 3);

    return 0;

}

 

// function definition

void add(int a, int b) {

    cout << (a + b);

}

Trong đoạn code trên, hàm nguyên mẫu là:

void add(int, int);

Nó cung cấp cho trình biên dịch thông tin về tên hàm và các tham số của hàm. Đó là lý do tại sao chúng ta có thể sử dụng mã để gọi một hàm trước khi hàm đã được xác định.

Cú pháp của một hàm nguyên mẫu là:

returnType functionName(dataType1, dataType2, …);

Ví dụ 4: Hàm nguyên mẫu trong C++

// using function definition after main() function

// function prototype is declared before main()

 

#include <iostream>

 

using namespace std;

 

// function prototype

int add(int, int);

 

int main() {

    int sum;

 

    // calling the function and storing

    // the returned value in sum

    sum = add(100, 78);

 

    cout << “100 + 78 = “ << sum << endl;

 

    return 0;

}

 

// function definition

int add(int a, int b) {

    return (a + b);

}

Đầu ra

100 + 78 = 178

Chương trình trên gần giống với ví dụ 3. Chỉ là là ở đây hàm được định nghĩa sau lời gọi hàm.

Đó lý do tại sao tôi đã sử dụng một hàm nguyên mẫu trong ví dụ này.

Lợi ích của việc sử dụng hàm do người dùng xác định:

  • Các hàm làm cho code có thể sử dụng lại. Ta có thể khai báo chúng một lần và sử dụng chúng nhiều lần
  • Các hàm làm cho chương trình dễ hơn vì mỗi tác vụ nhỏ được chia thành một hàm
  • Các hàm làm chương trình dễ đọc hơn.

1.6. Hàm trong thư viện C++

Các hàm trong thư viện là các hàm có sẵn trong lập trình C++.

Người lập trình có thể sử dụng các hàm này bằng cách gọi các hàm trực tiếp mà không cần phải tự viết hàm.

Một số hàm thư viện phổ biến trong C++ là sqrt(), abs(), isdigit(), v.v…

Để sử dụng các hàm thư viện, chúng ta thường cần tệp tiêu đề trong đó các hàm thư viện sẽ được định nghĩa.

Ví dụ, để sử dụng các hàm toán học như sqrt() và abs(), ta cần tệp tiêu đề cmath.

Ví dụ 5: Chương trình C++ tìm căn bậc hai của một số

#include <iostream>

#include <cmath>

using namespace std;

 

int main() {

    double number, squareRoot;

    

    number = 25.0;

 

    // sqrt() is a library function to calculate the square root

    squareRoot = sqrt(number);

 

    cout << “Square root of “ << number << ” = “ << squareRoot;

 

    return 0;

}

Đầu ra

Square root of 25 = 5

Trong chương trình này, hàm thư viện sqrt() được sử dụng để tính căn bậc hai của một số.

Khai báo hàm của sqrt() được định nghĩ trong tệp tiêu đề cmath. Đó là lý do tại sao ta cần mã #include <cmath> để sử dụng hàm sqrt().

  1. Các loại hàm được người dùng định nghĩa trong C++

Để hiểu rõ hơn về các đối số và trả về trong các hàm, các hàm do người dùng định nghĩa có thể được phân loại thành:

  • Hàm không có đối số và không có giá trị trả về
  • Hàm không có đối số nhưng có giá trị trả về
  • Hàm có đối số nhưng không có giá trị trả về
  • Hàm có đối số và giá trị trả về

Hãy cùng xem xét trường hợp cần phải kiểm tra số nguyên tố, ta sẽ dùng 4 loại hàm do người dùng định nghĩa đã nêu ở trên để giải quyết vấn đề.

Ví dụ 1: Không có đối số và không có giá trị trả về

# include <iostream>

using namespace std;

 

void prime();

 

int main()

{

    // No argument is passed to prime()

    prime();

    return 0;

}

 

// Return type of function is void because value is not returned.

void prime()

{

 

    int num, i, flag = 0;

 

    cout << “Enter a positive integer enter to check: “;

    cin >> num;

 

    for(i = 2; i <= num/2; ++i)

    {

        if(num % i == 0)

        {

            flag = 1

            break;

        }

    }

 

    if (flag == 1)

    {

        cout << num << ” is not a prime number.”;

    }

    else

    {

        cout << num << ” is a prime number.”;

    }

}

Trong chương trình trên, prime() được gọi từ hàm main() không có đối số.

Prime() lấy số dương từ người dùng và kiểm tra xem số đó có phải là số nguyên tố hay không.

Vì kiểu trả về của prime() là void nên không có giá trị nào được trả về từ hàm.

Ví dụ 2: Không có đối số nhưng có giá trị trả về

#include <iostream>

using namespace std;

 

int prime();

 

int main()

{

    int num, i, flag = 0;

 

    // No argument is passed to prime()

    num = prime();

    for (i = 2; i <= num/2; ++i)

    {

        if (num%i == 0)

        {

            flag = 1;

            break;

        }

    }

 

    if (flag == 1)

    {

        cout<<num<<” is not a prime number.”;

    }

    else

    {

        cout<<num<<” is a prime number.”;

    }

    return 0;

}

 

// Return type of function is int

int prime()

{

    int n;

 

    printf(“Enter a positive integer to check: “);

    cin >> n;

 

    return n;

}

Trong chương trình trên, hàm prime() được gọi từ hàm main() không có đối số.

prime() nhận một số nguyên dương từ người dùng. Vì kiểu trả về của hàm là int, nên nó trả về số đã nhập từ người trở lại hàm main() đang gọi.

Sau đó, số đó được kiểm tra xem có phải là số nguyên tố không trong main() và được xuất ra màn hình.

Ví dụ 3: Có đối số nhưng không có giá trị trả về

#include <iostream>

using namespace std;

 

void prime(int n);

 

int main()

{

    int num;

    cout << “Enter a positive integer to check: “;

    cin >> num;

    

    // Argument num is passed to the function prime()

    prime(num);

    return 0;

}

 

// There is no return value to calling function. Hence, return type of function is void. */

void prime(int n)

{

    int i, flag = 0;

    for (i = 2; i <= n/2; ++i)

    {

        if (n%i == 0)

        {

            flag = 1;

            break;

        }

    }

 

    if (flag == 1)

    {

        cout << n << ” is not a prime number.”;

    }

    else {

        cout << n << ” is a prime number.”;

    }

}

Trong chương trình trên, đầu tiên số dương được yêu cầu từ người dùng, được lưu trữ trong biến num.

Sau đó, num được chuyển đến hàm prime(), tại đây số đó sẽ được kiểm tra có phải là số nguyên tố hay không và xuất ra.

Vì kiểu trả về của prime() là void nên không có giá trị nào được trả về từ hàm.

Ví dụ 4: Có đối số và giá trị trả về

#include <iostream>

using namespace std;

 

int prime(int n);

 

int main()

{

    int num, flag = 0;

    cout << “Enter positive integer to check: “;

    cin >> num;

 

    // Argument num is passed to check() function

    flag = prime(num);

 

    if(flag == 1)

        cout << num << ” is not a prime number.”;

    else

        cout<< num << ” is a prime number.”;

    return 0;

}

 

/* This function returns integer value.  */

int prime(int n)

{

    int i;

    for(i = 2; i <= n/2; ++i)

    {

        if(n % i == 0)

            return 1;

    }

 

    return 0;

}

Trong chương trình trên, một số nguyên dương được yêu cầu từ người dùng và được lưu trữ trong biến num.

Sau đó, num được chuyển đến hàm prime() và kiểm tra xem có phải là số nguyên tố hay không.

Vì kiểu trả về của prime() là int, 1 hoặc 0 được trả về cho hàm main(). Nếu số đó là số nguyên tố, 1 sẽ được trả về. Nếu không, 0 sẽ được trả về.

Quay lại hàm main(), 1 hoặc 0 được trả về sẽ được lưu trữ trong biến flag và nội dung tương ứng sẽ được xuất ra màn hình.

Phương pháp nào tốt hơn?

Cả 4 chương trình trên đều cho đầu ra như nhau và đều là chương trình đúng kỹ thuật.

Không có quy tắc nào trong việc nên chọn phương pháp nào. Tùy vào tình huống và cách bạn muốn giải quyết vấn đề mà lựa chọn phương pháp cho phù hợp.

  1. Nạp chồng hàm trong C++ – Function overloading

Trong C++, hai hàm có thể có cùng tên nếu số lượng và/ hoặc kiểu đối số được truyền khác nhau.

Các hàm có cùng tên nhưng đối số khác nhau này được gọi là các hàm nạp chồng. Ví dụ:

// same number different arguments

int test() { }

int test(int a) { }

float test(double a) { }

int test(int a, double b) { }

Đây là 4 hàm được nạp chồng.

Lưu ý rằng kiểu trả về của cả 4 hàm này đều không giống nhau. Hàm được nạp chồng có thể có hoặc không có kiểu trả về khác nhau nhưng chúng phải có đối số khác nhau. Ví dụ:

// Error code

int test(int a) { }

double test(int b){ }

Ở đây, cả hai hàm đều có cùng tên, cùng kiểu và cùng số lượng đối số. Do đó, trình biên dịch đã xuất hiện lỗi.

3.1. Nạp chồng hàm bằng các loại tham số khác nhau

// Program to compute absolute value

// Works for both int and float

 

#include <iostream>

using namespace std;

 

// function with float type parameter

float absolute(float var){

    if (var < 0.0)

        var = -var;

    return var;

}

 

// function with int type parameter

int absolute(int var) {

     if (var < 0)

         var = -var;

    return var;

}

 

int main() {

    

    // call function with int type parameter

    cout << “Absolute value of -5 = “ << absolute(-5) << endl;

 

    // call function with float type parameter

    cout << “Absolute value of 5.5 = “ << absolute(5.5f) << endl;

    return 0;

}

Đầu ra

Absolute value of -5 = 5

Absolute value of 5.5 = 5.5

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-overloading-parameters-type.png

Trong chương trình này, tôi đã nạp chồng hàm absolute(). Dựa trên kiểu tham số được truyền trong quá trình gọi hàm, hàm tương ứng được gọi.

3.2. Nạp chồng hàm bằng số lượng tham số khác nhau

#include <iostream>

using namespace std;

 

// function with 2 parameters

void display(int var1, double var2) {

    cout << “Integer number: “ << var1;

    cout << ” and double number: “ << var2 << endl;

}

 

// function with double type single parameter

void display(double var) {

    cout << “Double number: “ << var << endl;

}

 

// function with int type single parameter

void display(int var) {

    cout << “Integer number: “ << var << endl;

}

 

int main() {

 

    int a = 5;

    double b = 5.5;

 

    // call function with int type parameter

    display(a);

 

    // call function with double type parameter

    display(b);

 

    // call function with 2 parameters

    display(a, b);

 

    return 0;

}

Đầu ra

Integer number: 5

Float number: 5.5

Integer number: 5 and double number: 5.5

Ở đây, hàm display() được gọi 3 lần với các đối số khác nhau. Tùy thuộc vào số lượng và kiểu đối số được truyền vào mà hàm display() tương ứng được gọi.

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-overloading-parameters-number.png

Kiểu trả về của tất cả các hàm này là giống nhau nhưng không phải là trường hợp nạp chồng hàm.

Lưu ý: Trong C++, nhiều hàm thư viện được nạp chồng. Ví dụ, hàm sqrt() có thể nhận double, float, int, v.v…. làm tham số. Điều này có thể xảy ra vì hàm sqrt() được nạp chồng trong C++.

  1. Đối số mặc định trong C++

Trong lập trình C++, ta có thể cung cấp các giá trị mặc định cho tham số của hàm.

Nếu một hàm có đối số mặc định được gọi mà không truyền đối số thì các tham số mặc định sẽ được sử dụng.

Tuy nhiên, nếu các đối số được truyền trong khi gọi hàm, các đối số mặc định sẽ bị bỏ qua.

4.1. Hoạt động của đối số mặc định

https://cdn.programiz.com/sites/tutorial2program/files/cpp-default-parameters.png

Ta có thể hiểu hoạt động của các đối số mặc định từ hình trên:

  • Khi temp() được gọi, cả 2 tham số mặc định đều được hàm sử dụng
  • Khi temp(6) được gọi, đối số đầu tiên trở thành 6 trong khi giá trị mặc định được sử dụng cho tham số thứ 2.
  • Khi temp(6, -2.3) được gọi, cả 2 tham số mặc định đều bị bỏ qua, dẫn đến i = 6  và f = -2.3
  • Khi temp(3.4) được truyền, hàm sẽ hoạt động theo cách không mong muốn vì không thể truyền đối số thứ 2 khi không truyền đối số đầu tiên.

Do đó, 3.4 được chuyển làm đối số đầu tiên. Vì đối số đầu tiên đã được định nghĩa là int, nên giá trị thực sự được truyền là 3.

4.2. Ví dụ: Đối số mặc định

#include <iostream>

using namespace std;

 

// defining the default arguments

void display(char = ‘*’, int = 3);

 

int main() {

    int count = 5;

 

    cout << “No argument passed: “;

    // *, 3 will be parameters

    display(); 

    

    cout << “First argument passed: “;

     // #, 3 will be parameters

    display(‘#’); 

    

    cout << “Both arguments passed: “;

    // $, 5 will be parameters

    display(‘$’, count); 

 

    return 0;

}

 

void display(char c, int count) {

    for(int i = 1; i <= count; ++i)

    {

        cout << c;

    }

    cout << endl;

}

Đầu ra

No argument passed: ***

First argument passed: ###

Both arguments passed: $$$$$

Đây là cách chương trình này hoạt động:

  • display() được gọi mà không truyền bất kỳ đối số nào. Trong trường hợp này, display() sử dụng cả hai tham số mặc định c = “*” và n = 1
  • display(‘#’) chỉ được gọi với một đối số. Trong trường hợp này, giá trị đầu tiên trở thanh “#”. Tham số mặc định thứ hai n = 1 được giữ lại
  • display(‘#’, count) được gọi với cả hai đối số. Trong trường hợp này, các đối số mặc định không được sử dụng.

Chúng ta cũng có thể xác định các tham số mặc định trong chính định nghĩa hàm. Chương trình dưới đây cũng tương đương với chương trình trên:

#include <iostream>

using namespace std;

 

// defining the default arguments

void display(char c = ‘*’, int count = 3) {

    for(int i = 1; i <= count; ++i) {

        cout << c;

    }

    cout << endl;

}

 

int main() {

    int count = 5;

 

    cout << “No argument passed: “;

    // *, 3 will be parameters

    display(); 

    

    cout << “First argument passed: “;

     // #, 3 will be parameters

    display(‘#’); 

    

    cout << “Both argument passed: “;

    // $, 5 will be parameters

    display(‘$’, count); 

 

    return 0;

}

Những điều cần nhớ

  1. Khi cung cấp giá trị mặc định cho một tham số, tất cả các tham số tiếp theo cũng phải có giá trị mặc định. Ví dụ:

// Invalid

void add(int a, int b = 3, int c, int d);

 

// Invalid

void add(int a, int b = 3, int c, int d = 4);

 

// Valid

void add(int a, int c, int b = 3, int d = 4);

  1. Nếu ta đang xác định đối số mặc định trong định nghĩa hàm thay vì nguyên mẫu hàm, thì hàm phải được định nghĩa trước khi gọi hàm.

// Invalid code

 

int main() {

    // function call

    display();

}

 

void display(char c = ‘*’, int count = 5) {

    // code

}

  1. Lớp lưu trữ trong C++

Mọi biến trong C++ đều có hai tính năng: kiểu và lớp lưu trữ.

Kiểu chỉ định kiểu dữ liệu có thể được lưu trữ trong một biến. Ví dụ: int, float, char, v.v…

Và lớp lưu trữ kiểm soát hai thuộc tính khác nhau của một biến, đó là: thời gian tồn tại của biến (xác định thời gian tồn tại của biến) và phạm vi (xác định phần nào của chương trình có thể truy cập).

Tùy thuộc vào lớp lưu trữ của một biến mà nó được thành 4 loại chính:

  • Biến cục bộ
  • Biến toàn cục
  • Biến cục bộ tĩnh
  • Biến thanh ghi
  • Lưu nhớ cục bộ trên luồng

5.1. Biến cục bộ

Một biến được xác định bên trong một hàm (bên trong thân hàm giữa các dấu ngoặc nhọn) được gọi là biến cục bộ hoặc biến tự động.

Phạm vi của nó chỉ giới hạn trong chức năng mà nó được xác định. Nói một cách dễ hiểu, biến cục bộ tồn tại và chỉ có thể được truy cập bên trong một hàm.

Tuổi thọ của một biến cục bộ kéo dài đến khi hàm thoát.

Ví dụ 1: Biến cục bộ

#include <iostream>

using namespace std;

 

void test();

 

int main() 

{

    // local variable to main()

    int var = 5;

 

    test();

    

    // illegal: var1 not declared inside main()

    var1 = 9;

}

 

void test()

{

    // local variable to test()

    int var1;

    var1 = 6;

 

    // illegal: var not declared inside test()

    cout << var;

}

Biến var không thể được sử dụng bên trong test() và var1 không thể sử dụng bên trong hàm main().

Từ khóa auto cũng được sử dụng để xác định các biến cục bộ trước đây như: auto int var.

Tuy nhiên, sau C++11, auto có một ý nghĩa khác và không nên được sử dụng để xác định các biến cục bộ.

5.2. Biến toàn cục

Nếu một biến được định nghĩa bên ngoài tất cả các hàm thì nó được gọi là biến toàn cục.

Phạm vi của một biến toán cục là toàn bộ chương trình.

Điều này có nghĩa là nó có thể được sử dụng và thay đổi ở bất kỳ phần nào của chương trình sau khi khai báo.

Tương tự như vậy, vòng đời của nó chỉ kết thúc khi chương trình kết thúc.

Ví dụ 2: Biến toàn cục

#include <iostream>

using namespace std;

 

// Global variable declaration

int c = 12;

 

void test();

 

int main()

{

    ++c;

 

    // Outputs 13

    cout << c <<endl;

    test();

 

    return 0;

}

 

void test()

{

    ++c;

 

    // Outputs 14

    cout << c;

}

Đầu ra

13

14

Trong chương trình trên, c là một biến toàn cục.

Biến này hiển thị cho cả hàm main() và test() trong chương trình trên.

5.3. Biến cục bộ tĩnh

Từ khóa static được sử dụng để chỉ định một biến tĩnh. Ví dụ:

… .. …

int main()

{

   static float a;

   … .. …

}

Biến cục bộ tĩnh chỉ tồn tại bên trong một hàm mà nó được khai báo (tương tự như biến cục bộ) nhưng thời gian tồn tại của nó bắt đầu khi hàm được gọi và chỉ kết thúc khi chương trình kết thúc.

Sự khác biệt chính giữa biến cục bộ và biến tĩnh là giá trị của biến tĩnh vẫn tồn tại ở cuối chương trình.

Ví dụ 3: Biến cục bộ tĩnh

#include <iostream>

using namespace std;

 

void test()

{

    // var is a static variable

    static int var = 0;

    ++var;

 

    cout << var << endl;

}

 

int main()

{

    

    test();

    test();

 

    return 0;

}

Đầu ra

1

2

Trong chương trình trên, hàm test() được gọi hai lần.

Trong lần gọi đầu tiên, biến var được khai báo là biến tĩnh và được khởi tạo bằng 0. Sau đó, 1 được thêm vào var hiển thị trên màn hình.

Khi hàm test() trả về, biến var vẫn tồn tại vì nó là một biến tĩnh.

Trong lần gọi hàm thứ hai, không có biến var mới nào được tạo. Var tương tự được tăng lên 1 và sau đó hiển thị ra màn hình.

Nếu var không được chỉ định thì đầu ra của chương trình trên là biến tĩnh.

1

1

5.4. Biến thanh ghi (không được dùng trong C++11)

Từ khóa register được sử dụng để chỉ định các biến thanh ghi.

Các biến thanh ghi tương tự như các biến tự động và chỉ tồn tại bên trong một hàm cụ thể. Nó được cho là nhanh hơn các biến cục bộ.

Nếu một chương trình gặp một biến thanh ghi, nó sẽ lưu trữ biến đó trong thanh ghi của bộ xử lý chứ không phải trong bộ nhớ (nếu có). Điều này làm cho biến thanh ghi nhanh hơn các biến cục bộ.

Tuy nhiên, từ khóa này không được chấp nhận trong C++11 và không nên được sử dụng.

5.5. Lưu trữ cục bộ trên luồng

Lưu trữ cục bộ luồng là một cơ chế mà các biến được cấp phát sao cho có một phiên bản của biến trên mỗi luồng còn tồn tại.

Từ khóa thread_load được sử dụng cho mục đích này.

  1. Đệ quy trong C++ – Recursion

Một hàm gọi chính nó được gọi là một hàm đệ quy. Và kỹ thuật này cũng được biết đến là kỹ thuật đệ quy.

6.1.hoạt động của đệ quy trong C++

void recurse()

{

    … .. …

    recurse();

    … .. …

}

 

int main()

{

    … .. …

    recurse();

    … .. …

}

Hình dưới đây cho ta thấy được cách hoạt động của đệ quy bằng cách gọi đi gọi lại chính nó.

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-recursion-working.png

Đệ quy tiếp tục cho đến khi một số điều kiện được đáp ứng.

Để ngăn chặn đệ quy vô hạn, câu lệnh if… else (hoặc những cách tiếp cận tương tự) có thể được sử dụng khi một nhánh thực hiện lệnh gọi đệ quy và nhánh kia thì không.

Ví dụ 1: Giai thừa của một số sử dụng đệ quy

// Factorial of n = 1*2*3*…*n

 

#include <iostream>

using namespace std;

 

int factorial(int);

 

int main() {

    int n, result;

 

    cout << “Enter a non-negative number: “;

    cin >> n;

 

    result = factorial(n);

    cout << “Factorial of “ << n << ” = “ << result;

    return 0;

}

 

int factorial(int n) {

    if (n > 1) {

        return n * factorial(n – 1);

    } else {

        return 1;

    }

}

Đầu ra

Enter a non-negative number: 4

Factorial of 4 = 24

Hoạt động của chương trình giai thừa

https://cdn.programiz.com/sites/tutorial2program/files/cpp-function-recursion-example.png

Như ta có thể thấy, hàm giai thừa factorial() đang gọi chính nó. Tuy nhiên, trong mỗi lần gọi, tôi đã giảm giá trị của n đi 1. Khi n nhỏ hơn 1, cuối cùng hàm factorial() trả về kết quả.

6.2. Ưu điểm và nhược điểm của đệ quy

Ưu điểm:

  • Đệ quy làm cho code của chúng ta ngắn và gọn gàng hơn
  • Đệ quy được yêu cầu trong các bài toán liên quan đến cấu trúc dữ liệu và các thuật toán nâng cao, chẳng hạn như đồ thị và duyệt cây.

Nhược điểm:

  • Đệ quy chiếm nhiều không gian ngăn xếp so với một chương trình lặp lại
  • Đệ quy dùng nhiều thời gian xử lý hơn.
  • Đệ quy có thể khó sửa lỗi hơn so với một chương trình lặp lại tương đương.
  1. Hàm trả về tham chiếu

Trong lập trình C++, bạn không chỉ có thể chuyển các giá trị bằng cách tham chiếu đến một hàm mà còn có thể trả về một giá trị bằng tham chiếu.

Để hiểu được tính năng này, bạn cần có kiến thức về biến toàn cục.

Ví dụ:

#include <iostream>

using namespace std;

 

// Global variable

int num;

 

// Function declaration

int& test();

 

int main()

{

    test() = 5;

 

    cout << num;

 

    return 0;

}

 

int& test()

{

    return num;

}

Đầu ra

5

Trong chương trình trên, kiểu trả về của hàm test() là int&. Do đó, hàm này trả về một tham chiếu của biến num.

Câu lệnh trả về là return num;. Khác với trả về theo giá trị, câu lệnh này không trả về giá trị của num, thay vào đó nó trả về chính biến của nó.

Vì vậy, khi biến được trả về, nó có thể được gán một giá trị như được thực hiện trong test() = 5

Nó lưu trữ 5 vào biến num được hiển thị trên màn hình.

Những điều quan trọng cần nhớ khi trả về bằng tham chiếu

  • Hàm thông thường trả về giá trị nhưng hàm này thì không. Do đó, bạn không thể trả về một hằng số từ hàm.

int& test() {

    return 2;

}

  • Không thể trả về một biến cục bộ từ hàm này.

int& test()

{

    int n = 2; 

    return n; 

}

 

 

Để lại một câu trả lời