Các khái niệm về scope (phạm vi), duration (thời lượng) và linkage (mối liên kết) thường gây ra nhiều nhầm lẫn về ý nghĩa của chúng. Chúng ta sẽ cùng tìm hiểu qua bài viết này để hiểu rõ và phân biệt được các khái niệm này.
1. Scope (phạm vi)
Phạm vi của một identifier quy định những vị trí mà identifier đó được truy cập hay sử dụng. Có 4 loại scope trong ngôn ngữ C:
+ Function scope: Áp dụng cho các identifier được khai báo bên trong function, và được giải phóng khi hàm kết thúc việc thực thi. Mỗi hàm không thể có nhiều identifier cùng tên, nhưng các identifier cùng tên có thể được sử dụng cho nhiều hàm. Bao gồm:
- Local variable: biến cục bộ
- Function parameter: tham số hàm
1 2 3 4 5 6 7 8 9 10 11 12 13 |
int addNumbers(int a, int b) // function definition { int result; result = a+b; return result; // return statement } int subtractNumbers(int a, int b) { int result; result = a>b?(a-b):(b-a); return result; } |
Tham số hàm a, b và biến cục bộ result được khởi tạo giá trị trong stack khi hàm được gọi và được giải phóng giá trị ngay khi kết thúc hàm. Các tham số và các biến cục bộ của 2 hàm trên có tên trùng nhau hoàn toàn hợp lệ.
+ Block/local scope: Các identifier được khai báo bên trong một block (tập hợp các câu lệnh được đặt trong cặp dấu { }), được sử dụng ngay sau khi được khai báo cho đến khi kết thúc block (bao gồm cả các khối lồng nhau).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int AddSubtractNumbers(int a, int b) { { //begin of block int result; result = a+b; printf("The result of add is %d\n", result); } //end of block { int result; result = a>b?(a-b):(b-a); printf("The result of subtract is %d\n", result); } } |
+ Function prototype scope: một ví dụ đơn giản về phạm vi khai báo nguyên mẫu hàm:
1 2 3 |
void init(int i, int i); //invalid void init(int i, int j); //valid |
Đối với ví dụ hợp lệ trên, biến i và j được khai báo ngay bên trong câu lệnh khai báo nguyên mẫu hàm và sẽ được giải phóng ngay khi câu lệnh kết thúc.
+ File scope: Bất kỳ cái tên nào được định nghĩa bên ngoài các functions sẽ có phạm vi của file scope. Chúng có thể được sử dụng ngay sau khi được khai báo cho đến khi kết thúc file. File ở đây được hiểu là file source đã được biên dịch, tất cả các chỉ thị tiền xử lý đã được xử lý.
2. Duration (thời lượng)
Thời lượng của một identifier sẽ xác định khi nào identifier đó được tạo và được hủy. Có 3 loại:
+ Static duration: đối tượng được phân bổ vĩnh viễn, nghĩa là thời gian hoạt động cho đến khi chương trình kết thúc thực thi. Ví dụ: biến toàn cục, biến cục bộ static
+ Automatic duration: thời gian hoạt động từ lúc được khai báo cho đến khi thoát khỏi khối block chứa chúng. Ví dụ: biến cục bộ, tham số hàm
+ Dynamic duration: hoạt động tạo và hủy của đối tượng sẽ phụ thuộc vào yêu cầu người lập trình. Ví dụ: cấp phát bộ nhớ động.
3. Linkage (liên kết)
Linkage được sử dụng để quyết định rằng nhiều khai báo identifier cùng tên (trong các scope khác nhau) có tham chiếu đến cùng một đối tượng (vị trí bộ nhớ) hay đến các đối tượng riêng biệt hay không. Có 3 loại linkage:
+ no linkage: identifier chỉ tham chiếu đến chính nó, không có bất cứ liên kết gì đến các identifier khác cùng tên. Ví dụ:
- Biến cục bộ
- Biến mang kiểu dữ liệu người dùng tự định nghĩa (struct, enum) được khai báo trong block.
+ internal linkage: identifier chỉ có thể được truy cập tại bất cứ vị trí nào trong file mà nó được khai báo, nghĩa là các identifier cùng tên trong một file sẽ truy cập và lưu trữ dữ liệu tại một địa chỉ nhất định. Ví dụ:
- Biến toàn cục static
- Hàm static
- Biến toàn cục const
+ external linkage: identifier có thể được truy cập tại bất cứ vị trí nào ngay cả bên trong file chứa khai báo và các file khác bên ngoài có liên kết với nhau. Ví dụ:
- Hàm chức năng
- Biến toàn cục (không phải const)
- Biến toàn cục static const
- Biến toàn cục inline const
Bảng 1. Scope, duration và likage của các identifiers
Các loại identifier | Ví dụ | Scope | Duration | Linkage |
Biến cục bộ | int x; | Block | Automatic | None |
Biến cục bộ static | static int s_x; | Block | Static | None |
Biến cục bộ cấp phát động | int* x = (int*)malloc(10); | Block | Dynamic | None |
Tham số hàm | void foo(int x) | Block | Automatic | None |
Biến toàn cục (không phải const) | int g_x; | File | Static | External |
Biến toàn cục static (không phải const) | static int g_x; | File | Static | Internal |
Biến toàn cục const | int const g_x; | File | Static | External |
Biến toàn cục static const | static int const g_x; | File | Static | Internal |
Biến toàn cục extern const | extern int const g_x; | File | Static | External |