CHPV C++ | Phân biệt biến Local, Global, Extern, Static và Const

MỤC LỤC:
Biến (variable) là tên của một vùng trong bộ nhớ RAM, được sử dụng để lưu trữ thông tin. 
dưới đây là cách tiếp cận dễ hiểu nhất cho những ai muốn phân biệt biến Local, Global, Extern, Static và Const

Biến Local

- Biến local chỉ xuất hiện và tồn tại trong một phạm vi (scope) cụ thể.

- Biến bị tự động hủy khi ra khỏi phạm vi sử dụng.

Nội dung hàm cũng là một phạm vi, vậy nên biến local chỉ tồn tại trong hàm mà biến được khai báo.

#include <iostream>
using namespace std
void Display();
int main()
{
    Display();
    std::cout << i; // error, bởi vì biến i chỉ tồn tại trong scope khai báo hàm Display
    getch();
}
 
void Display()
{ 
    int i; // biến i bắt đầu được khai báo trong scope
    for(i = 0; i < 10; i++) 
        cout << "in ra i: " << i << endl;
} // điểm kết thúc phạm vi của biến i

Biến Global

- Biến global là biến được khai báo bên ngoài tất cả các hàm, có hiệu lực từ thời điểm nó được khai báo và có có thể sử dụng trong tất cả các hàm trong chương trình. 

- Biến global tồn tại đến khi chương trình bị kết thúc.

- Đặc biệt, ta có thể khai báo 1 biến global trong 1 file (.c/.cpp/.h) và truy cập biến này từ 1 file (.c/.cpp/.h) khác. Chú ý, biến phải được khai báo ở cả 2 file và từ khóa extern phải được thêm trong lần khai báo thứ 2.

#include <conio.h>
#include <stdio.h>
 
int a = 0; // Bien Global a
 
void Display();
 
int main()
{
    Display();
    printf("\na = %d", a);
    getch();
}
void Display()
{
    for(a = 0; a < 3; a++)
        printf("\na = %d", a);
}

Trên là ví dụ về biến a được khai báo global khác với biến a trong vòng lặp thuộc phạm vi hàm Display()

Biến Extern

- Biến extern tham chiếu của một biến, đã được định nghĩa bên ngoài (ở một file source khác). 

- Nó chỉ mang ý nghĩa khai báo (declare) chứ không định nghĩa (define) ( tức là không xin cấp phát thêm bộ nhớ cho biến, bởi biến này đã tồn tại ở dạng toàn cục, được khai báo ở một nơi nào đó ).

- Biến được tham chiếu phải được khai báo ở cấp độ cao nhất (toàn cục), và có thể nằm trong một file khác

Ví dụ: ở một fileA.h, ta có một biến và hàm global như sau: 

int bienGlobalA = 0; // implicit definition 
void hamGlobalB(); // function prototype (declaration) 
int hamGlobalB() { 
  bienGlobalA = 1;
}


Ví dụ: ở một fileB.h có biến đc khai báo extern như sau: 

(Chú ý: từ khóa extern không cho phép gán giá trị ban đầu cho biến, cũng phải thôi, bởi biến này đã coi như được khởi tạo ở một chỗ khác rồi)

extern int GlobalVariable; // implicit definition 
void hamGlobalB(); // function prototype (declaration)
void hamGlobalC(){
    hamGlobalB()
    bienGlobalA = bienGlobalA  + 1;
}  

Biến Static

Biến tĩnh (static variables) là biến được tạo ra duy nhất một lần khi gọi hàm lần đầu tiên và tiếp tục tồn trong suốt vòng đời của chương trình.

Biến static mang tính chất của 1 biến toàn cục (global), vừa có tính chất của 1 biến cục bộ (local):

Tính chất của biến toàn cục: khi khối lệnh chứa định nghĩa biến đó kết thúc, biến không bị hủy, vùng nhớ giành cho biến không bị thu hồi. Và nó vẫn nằm trong vùng nhớ của chương trình.
Và nó được tự động cập nhật khi khối lệnh đó được gọi lại.

void callStaticV() {
    static int bienA = 1000;
    bienA++;
    std::cout << "bienA: " << bienA;
}

Tính chất của biến cục bộ: biến chỉ có thể được sử dụng trong khối lệnh mà nó được khai báo.
( Ví dụ trong khối lệnh trên, nếu gọi biến bienA ngoài scope của hàm callStaticV, trình biên dịch lập tức sẽ báo lỗi  )

void callStaticV() {
    static int bien = 1000;
    bien++;
    std::cout << "bien: " << bien;
}

Dòng static int bien = 1000; chỉ được gọi duy nhất 1 lần. 

Sử dụng biến Static khi có nhu cầu giữ giá trị của biến trong chương trình.

Ví dụ: chương trình có chức năng tạo ID không trùng nhau đơn giản, hoặc đếm số lượng các đối tượng được tạo ra của một class trong chương trình. 

#include <iostream>
#include <string>
using namespace std;
int taoID()
{
	static int s_local_id(0);
	++s_local_id;
return s_local_id;
} int main() { int newID1 = taoID();
string strName1("tuan"); int newID2 = taoID();
string strName2("ti"); int newID3 = taoID();
string strName3("tien"); cout << newID1 << " : " + strName1 << endl; cout << newID2 << " : " + strName2 << endl; cout << newID3 << " : " + strName3 << endl; return 0; }

Biến Constants ( Hằng số trong C++ )

- Để định nghĩa những biến, những thành phần mà giá trị của nó không thay đổi trong suốt thời gian chạy của chương trình. 
Ví dụ: số PI = 3.14, 

- Nên định nghĩa hằng số ở một nơi, và sử dụng nó ở toàn chương trình. Việc đặt hằng số ở lẻ tẻ các file, khiến việc bảo trì mai sau rất bất tiện.

Ví dụ: 1 file MyConstants.h riêng để chứa khai báo hằng số cho cả chương trình:

#ifndef _MYCONSTANTS_
#define _MYCONSTANTS_

// Định nghĩa namespace chứa các hằng số
namespace myConstants 
{
	const double PI{ 3.1416 };	
	const double TOC_DO_AM_THANH{ 343.2 };
	const int SO_GIAY_CUA_1_PHUT{ 60 };
	// trong thực tế, bạn sẽ tự biết cần hằng số cho những thông tin nào, bởi nó có ý nghĩa cụ thể với chương trình của bạn.
}

#endif // !_MYCONSTANTS_

Và ta có thể sử dụng nó một cách an toàn và không sợ đụng hàng bởi name space của chúng ta main.cpp

#include <iostream>
#include "MyConstants.h"	// include file header hằng số để dùng
using namespace std;
//using namespace myConstants;
int main() { cout << "PI = " << myConstants::PI << endl;
return 0; }

- Có nhiều cách triển khai hằng số trong chương trình: 

( 1 ) Hằng số với từ khóa const: 

++ Phổ biến và dễ dàng sử dụng vì mang bản chất là một biến thông thường


( 2 ) Hằng số với chỉ thị tiền xử lý #define

Ví dụ: 

#include <iostream>
using namespace std;

// Định nghĩa số học sinh tối đa cần lấy thông tin
#define MAX_OF_STUDENT 50

int main()
{
        // NHƯỢC ĐIỂM: có thể bị ghi đè bởi define lại cùng tên
        #define MAX_OF_STUDENT 51
        cout << MAX_OF_STUDENT << endl; // => 51
        // làm điều gì đó với MAX_OF_STUDENT, ví dụ nhập thông tin từng học sinh
	return 0;
}


++ Ưu điểm: trực quan, và tối ưu hiệu suất hơn, dễ dàng bảo trì, nếu mã nguồn tốt, đôi khi chỉ cần thay đổi giá trị define, chương trình đã được đáp ứng với yêu cầu mới. ( giả dụ, sau này tăng MAX_OF_STUDENT lên 100 )

++ Nhược điểm: các macro luôn có phạm vi toàn cục, ví dụ đoạn mã dưới đây vẫn hợp lệ.

Ví dụ: 

#include <iostream>
using namespace std;

void callStaticV() {
    static int bien = 1000;
    bien++;
    qDebug() << "bien: " << bien;
#define MAX_OF_STUDENT 50
#define MAX_OF_STUDENT 51
}

int main()
{
        // NHƯỢC ĐIỂM: các macro luôn có phạm vi toàn cục
        cout << MAX_OF_STUDENT << endl; // => 51         // làm điều gì đó với MAX_OF_STUDENT, ví dụ nhập thông tin từng học sinh return 0; }


TuanTiTien++

Author:

Tôi là Tuấn Anh, một lập trình viên C++, hiện tại đang làm việc với Qt Framework. Giờ đã là bố của một thanh niên nhỏ, gần đây tôi thấy mình cần sống có trách nhiệm hơn nữa. : ]] Tôi cảm thấy nếu tôi không có nơi nào đó để ghi lại, tôi sẽ quên mất nhiều thứ. Dấu chân trên cát cũng vậy, nếu ta không chụp ảnh nó lại, rồi nó cũng sẽ bị gió làm mờ đi dần rồi mất hút hẳn. Cảm ơn anh em đã ghé qua và đọc những gì tôi viết. Hữu duyên thiên lý Ngô tương nặng, à nhầm Năng tương ngộ. : ]] Thân ái 3000!
© Giao diện website thiết kế bởi TuanTiTien.com