Bài 6: Biến Static, Biến Extern, Biến Register và Biến Volatile

Biến Static, Biến Extern, Biến Register và Biến Volatile

Biến Static, Biến Extern, Biến Register và Biến Volatile là các dạng đặc biệt khi khai báo biến. Đây là kiến thức nâng cao khi chúng ta sử dụng biến trong lập trình C. Cùng tìm hiểu cú pháp và cách sử dụng nhé.

Biến Static

Biến static hay biến tĩnh, được tạo ra bên trong một khối lệnh có khả năng lưu giữ giá trị của nó cho dù chương trình đã chạy ra bên ngoài khối lệnh chứa nó.

Khi 1 biến cục bộ được khai báo với từ khóa static. Biến sẽ chỉ được khởi tạo 1 lần duy nhất và tồn tại suốt thời gian chạy chương trình. Giá trị của nó không bị mất đi ngay cả khi kết thúc hàm.

Tuy nhiên khác với biến toàn cục có thể gọi trong tất cả mọi nơi trong chương trình, thì biến cục bộ static chỉ có thể được gọi trong nội bộ hàm khởi tạo ra nó. Mỗi lần hàm được gọi, giá trị của biến chính bằng giá trị tại lần gần nhất hàm được gọi.

Cú pháp: static <kiểu dữ liệu> <tên biến>;

Khi chưa sử dụng Static

Dưới đây là một ví dụ đơn giản cho thấy sự khác biệt giữa việc sử dụng biến cục bộ và biến static:

#include <stdio.h>
 
void incrementAndPrint()
{
    int value = 1; // automatic duration by default
    ++value;
    printf("Gia tri cua bien: %d \n", value);
} // value is destroyed here
 
int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
    
    return 0;
}

Kết quả

Mỗi khi hàm incrementAndPrint được gọi, một biến có tên gọi “value” được tạo ra và được gán giá trị 1.

Hàm incrementAndPrint sẽ tăng giá trị của biến “value” lên 1 đơn vị, giá trị in ra màn hình là 2.

Khi hàm incrementAndPrint thực hiện xong tác vụ, biến “value” bị hủy. Cứ thực hiện liên tiếp như vậy, kết quả in ra màn hình là:

Gia tri cua bien: 2 
Gia tri cua bien: 2 
Gia tri cua bien: 2

Khi sử dụng Static

Bây giờ, chúng ta thêm từ khóa static trước nơi biến “value” được khai báo:

#include <stdio.h>
 
void incrementAndPrint()
{
    static int value = 1; // automatic duration by default
    ++value;
    printf("Gia tri cua bien: %d \n", value);
} // value is destroyed here
 
int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();
    
    return 0;
}

Lần này, vì biến “s_value” được khai báo với từ khóa static, nó sẽ chỉ được khởi tạo một lần duy nhất, dù hàm incrementAndPrint được gọi nhiều lần.

Mỗi lần hàm incrementAndPrint được gọi, giá trị của biến “s_value” được tăng thêm 1, và còn được lưu giữ lại đó cho đến khi chương trình kết thúc.

Kết quả in ra như sau:

Gia tri cua bien: 2 
Gia tri cua bien: 3 
Gia tri cua bien: 4

Biến Extern

Trong C, khi 1 biến đi sau từ khóa “extern” có nghĩa là:

  • Nó là tham chiếu của một biến,hàm cùng tên nào đó, đã được định nghĩa bên ngoài. Nó chỉ khai báo chứ không định nghĩa ( cấp phát bộ nhớ cho biến ).
  • 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á

Trong C, một chương trình lớn có thể được chia thành các module nhỏ hơn, các module này có thể được biên dịch riêng lẻ và được liên kết lại với nhau. Điều này được thực hiện nhằm tăng tốc độ quá trình biên dịch các chương trình lớn.

Tuy nhiên, khi các module được liên kết, các tập tin phải được chương trình thông báo cho biết về các biến toàn cục được yêu cầu. Một biến toàn cục chỉ có thể được khai báo một lần. Nếu hai biến toàn cục có cùng tên được khai báo trong cùng một tập tin, một thông điệp lỗi ‘duplicate variable name’ (tên biến trùng) có thể được hiển thị hoặc đơn giản trình biên dịch C chọn một biến khác.

Để sử dụng được biến toàn cục ở một file khác, chúng ta phải khai báo lại biến và thêm từ khóa extern phía trước, để báo rằng biến này đã được khi báo ở file khác.

Cú pháp: extern <kiểu dữ liệu> <Tên Biến>;

Lưu ý: khi sử dụng extern, các bạn không được khai báo giá trị ban đầu cho biến

Ví dụ, file 1 ta khai báo

int GlobalVariable = 0; // implicit definition 
void SomeFunction(); // function prototype (declaration) 
int main() 
{ 
  GlobalVariable = 1; 
  SomeFunction(); 
  return 0; 
}

File 2, chúng ta extern biến đó để sử dụng

extern int GlobalVariable; // implicit definition 
void SomeFunction(); // function prototype (declaration) 
int main() 
{ 
  GlobalVariable = 1; 
  SomeFunction(); 
  return 0; 
}

 

Biến Register

Tác dụng của từ khóa register, nói một cách ngắn gọn là làm tăng hiệu năng(performance) của chương trình.

Cú pháp: register <kiểu dữ liệu> <tên biến>;

Để hiểu được điều đó thì trước hết thử xem tại sao hiệu năng nó tăng?
Quay lại với vấn đề cơ bản hơn một chút, trong kiến trúc của vi xử lý thì ALU (Arithmetic Logic Unit) là con trâu đóng vai trò xử lý các tính toán số học. Dữ liệu đưa vào làm việc với ALU phải chứa trong một vùng đặc biệt, gọi là các thanh ghi(register), và ALU chỉ làm việc với đống thanh ghi đó.

Trong khi đó các biến khai báo trong chương trình thì đặt ở bộ nhớ ngoài (RAM chẳng hạn …). Do đó với khai báo biến thông thường, để thực hiện một phép tính thì cần có 3 bước.

  1. Nạp giá trị từ vùng nhớ chứa biến vào register
  2. Yêu cầu ALU xử lý register vừa được nạp giá trị.
  3. Đưa kết quả vừa xử lý của ALU ra ngoài vùng nhớ chứa biến.

register

Khi thêm từ khóa register để khai báo biến, thì tức là ta đã yêu cầu trình biên dịch ưu tiên đặc biệt dành luôn vùng register để chứa biến đó. Và hiển nhiên khi thực hiện tính toán trên biến đó thì giảm được bước 1 và 3, giảm bớt thủ tục vậy nên hiệu năng tăng lên.

Biến Volatile

Volatile có nghĩa là không dự đoán được. Một biến sử dụng với volatile qualifier có nghĩa là nói với compiler là biến này có thể sẽ được thay đổi ở bất kì chỗ nào

Một biến cần được khai báo dưới dạng biến volatile khi nào? Khi mà giá trị của nó có thể thay đổi một cách không báo trước. Việc khai báo biến volatile là rất cần thiết để tránh những lỗi sai khó phát hiện do tính năng optimization của compiler.

Trong thực tế, có 3 loại biến mà giá trị có thể bị thay đổi như vậy:

  • Memory-mapped peripheral registers (thanh ghi ngoại vi có ánh xạ đến ô nhớ)
  • Biến toàn cục được truy xuất từ các tiến trình con xử lý ngắt (interrupt service routine)
  • Biến toàn cục được truy xuất từ nhiều tác vụ trong một ứng dụng đa luồng.

Cú pháp: volatile <kiểu dữliệu> <tên dữ liệu>;

Biến Volatile rất cần thiết trong lập trình nhúng, vì khi đó có các tác vụ như ngắt ảnh hưởng tới giá trị của biến. Trong lập trình C cơ bản thì rất ít gặp.

Kết

Hi vọng sau khi học xong bài này, các bạn sẽ biết sử dụng các loại biến là Biến Static, Biến Extern, Biến Register và Biến Volatile. Tiếp tục đến với bài tiếp theo trong serie Học lập trình C từ A tới Z 

Nếu thấy có ích hãy chia sẻ bài viết và tham gia nhóm Nghiện Lập Trình để giao lưu và học hỏi nhé.

 

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 *