Phạm vi của biến

Chào các bạn! Chúng ta cùng tiếp tục với bài học tiếp theo trong khóa học lập trình trực tuyến ngôn ngữ C++ hướng thực hành.

Trong các bài học trước, chúng ta đã cùng nhau tìm hiểu cách sử dụng biến (variable) gồm có cách khai báo, khởi tạo, nhập giá trị từ bàn phím và đưa vào biến, tính toán giá trị của biến và đưa giá trị của biến lên màn hình…

Khi một biến được khai báo, hệ điều hành sẽ cấp phát cho chương trình một vùng nhớ có độ lớn tương ứng với độ lớn kiểu dữ liệu của biến.

Vấn đề là không phải chỉ có một mình chương trình mà các bạn đang viết sử dụng các vùng nhớ trên RAM, mà còn nhiều chương trình khác đang chạy ngầm nữa.

Trong khi đó, bộ nhớ RAM của chúng ta chỉ có giới hạn. Vì thế, một khi biến (variable) không còn giá trị sử dụng nữa, chúng phải được tiêu hủy để trả lại vùng nhớ mà nó đang giữ, để cấp phát cho những ứng dụng khác cần sử dụng bộ nhớ.

Khi bạn kiểm soát được việc lúc nào cần khai báo biến, khi nào cần tiêu hủy biến sẽ giúp bạn quản lý tài nguyên máy tính tốt hơn. Điều này cần kĩ năng tổ chức và thiết kế chương trình, một kĩ năng quan trọng cần có thời gian để rèn luyện.

Trong bài học này, chúng ta sẽ tìm hiểu hai khái niệm luôn luôn gắn liền với biến (variable):

  • Phạm vi của biến.
  • Thời gian tồn tại của biến.

Hai khái niệm này thường có liên kết chặt chẽ với nhau.

Phạm vi của biến

Phạm vi của biến xác định nơi chúng ta có thể truy cập vào biến.

  • Biến được khai báo bên trong khối lệnh (block) được gọi là biến cục bộ (local variable).

Chương trình bên dưới minh họa cho việc khai báo biến cục bộ, truy cập và truy xuất giá trị của biến cục bộ.

Biến local variable được khai báo bên trong khối lệnh của hàm main, nên các câu lệnh truy xuất đến biến local variable hoàn toàn hợp lệ.

Một khối lệnh có thể chứa nhiều khối lệnh con khác nhau. Ví dụ:

Trong đoạn chương trình trên, chúng ta có thêm một khối lệnh nằm bên trong khối lệnh của hàm main, và xuất hiện một biến có tên local variable 2 được khai báo bên trong nó. Ở trong khối lệnh con này (khối lệnh nằm trong khổi lệnh của hàm main) chúng ta có thể truy xuất giá trị của biến local variable 2 như mình đã làm thông qua dòng lệnh

cout << "local_variable2: " << local_variable2 << endl;

để in giá trị của biến local variable 2 lên màn hình. Ngoài ra, mình còn sử dụng phép gán (với toán tử “=”) để sửa đổi giá trị cho biến local variable 1 vốn được định nghĩa bên ngoài khối lệnh con.

Điều này có nghĩa là chúng ta có thể truy cập đến một biến đã được khai báo trong những khối lệnh con bên dưới biến đó nếu những khối lệnh con này cũng được đặt trong khối lệnh chứa biến được khai báo.

Khi các bạn sử dụng các khối lệnh con, bạn có thể đặt tên biến trùng với biến được khai báo trong khối lệnh bên ngoài mà nó chứa khối lệnh con đó. Các bạn nhìn vào chương trình bên dưới để thấy rõ hơn:

Chương trình trên không hề vi phạm quy tắc đặt tên biến mà mình đã nói ở những bài trước.

Trong cùng một khối lệnh không được phép có hai biến trùng tên.

Trong chương trình trên, hai biến number of employees hoàn toàn được khai báo trong hai khối lệnh khác nhau. Bây giờ chúng ta chạy thử chương trình xem kết quả in ra trên màn hình như thế nào.

Như các bạn cũng đã thấy, khi mình thực hiện truy xuất giá trị của biến number of employees bên trong khối lệnh con thì chỉ lấy được giá trị của biến được khai báo bên trong khối lệnh con đó. Tương tự, khi mình thực hiện truy xuất giá trị của biến number of employees của khối lệnh sau hàm main thì chỉ lấy được giá trị của biến được khai báo trong khối lệnh sau hàm main.

Việc đặt tên biến trùng nhau trong nhiều khối lệnh lồng nhau được compiler của Visual studio cho phép, nhưng mình khuyên các bạn nên nghĩ ra một tên biến khác phù hợp hơn để tránh việc nhầm lẫn khi thiết kế một chương trình có quy mô lớn.

  • Biến được khai báo bên ngoài khối lệnh được gọi là biến toàn cục (global variable).

Các bạn cùng nhìn vào đoạn chương trình mẫu bên dưới để xem cách mình khai báo một biến toàn cục như thế nào.

Như các bạn thấy, mình không đặt dòng khai báo biến bên trong khối lệnh của hàm main nữa mà mình đặt nó bên ngoài và nằm trên khối lệnh của hàm main.

Trong bài học đầu tiên, mình có nói về việc khối lệnh của hàm main sẽ là nơi mà chương trình bắt đầu thực thi, ngoại trừ một số câu lệnh đặc biệt có thể đặt ngoài khối lệnh (khai báo biến, include thư viện, gọi namespace, định nghĩa các class, …).

Khi có một biến khác nằm trong phạm vi của khối lệnh hàm main được khai báo cùng tên với biến toàn cục bên ngoài khối lệnh hàm main, mỗi câu lệnh truy xuất đến biến đó đều được ưu tiên tìm đến biến cục bộ bên trong hàm main trước. Vậy có cách nào để ta truy xuất được biến toàn cục bên ngoài hàm main không?

Câu trả lời là có! Chúng ta sử dụng toán tử phạm vi (::) như sau:

#include <iostream>
using namespace std;

int value = 1;

int main()	{

	int value = 10;

	cout << "local value: " << value << endl;
	cout << "global value: " << ::value << endl;
	
	system("pause");
	return 0;
}

Các bạn thử chạy đoạn code trên xem chương trình thông báo kết quả như thế nào nhé.

Một khi biến toàn cục đã được khai báo, chúng có thể được truy cập tại mọi khối lệnh nằm bên dưới nó. Trong khi đó, biến cục bộ chỉ được phép truy cập khi dòng lệnh còn đặt bên trong khối lệnh chứa nó. Ví dụ:

Chương trình báo lỗi biến local_variable không được khai báo trước đó trong khi mình đã khai báo bên trong khối lệnh con của khối lệnh hàm main.

Nguyên nhân là do biến local_variable đã bị tiêu hủy trước khi mình kịp truy xuất đến nó.

Thời gian tồn tại của biến

Đối với những biến cục bộ (local variable) có kiểu dữ liệu thông thường như các bạn đã học trong những bài trước, vùng nhớ của biến sẽ tự động giải phóng khi ra khỏi khối lệnh chứa nó.

Việc cố gắng truy cập đến một biến đã bị hủy sẽ gây nên lỗi. Thời gian tồn tại của biến cục bộ phụ thuộc vào của khối lệnh chứa nó.

Đối với biến toàn cục (được khai báo bên ngoài khối lệnh của hàm main), nó sẽ tồn tại cho đến khi chương trình kết thúc hoặc bị kết thúc bởi người dùng.

Vì thế, các bạn chỉ nên sử dụng biến toàn cục khi cần thiết, để tránh việc vùng nhớ của biến toàn cục được cấp phát nhưng bị chiếm giữ quá lâu gây ảnh hưởng đến việc cấp phát bộ nhớ cho những chương trình khác.


Tổng kết

  • Biến cục bộ được khai báo bên trong khối lệnh. Những biến này chỉ được phép truy cập ở bên trong khối lệnh đó. Biến cục bộ sẽ bị hủy tại thời điểm kết thúc khối lệnh.

  • Biến toàn cục được khai báo bên ngoài khối lệnh. Những biến này được phép truy cập trong mọi khối lệnh nằm bên dưới nó. Biến toàn cục chỉ bị hủy khi chương trình kết thúc.


Hẹn gặp lại các bạn trong bài học tiếp theo trong khóa học lập trình C++ hướng thực hành.

Mọi ý kiến đóng góp hoặc thắc mắc có thể đặt câu hỏi trực tiếp tại diễn đàn
www.daynhauhoc.com


Link Videos khóa học

11 Likes

các anh cho em hỏi tại sao khi khai báo biến toàn cục thì lúc xuất giá trị của nó trong hàm main thì giá trị của nó lại bằng 0 mà không phải giá trị rác ạ ?

1 Like

Em cũng có câu hỏi giống anh Hiếu:
Mình chưa dùng tới giá trị hàm main () sao lại phải return giá trị hàm main () về 0 ạ?

Người ta return 0 để coi như báo hiệu chương trình chạy không sinh lỗi.

Bạn không cần giá trị của hàm main, nhưng máy cần đến nó.

Câu hỏi của bạn không hề giống với câu hỏi của bạn Lê Hiếu.

3 Likes


Mình có khai báo biến local_variable1 ở đầu sau hàm main và mình có gán lại ở trong khối lệnh con. Nhưng lúc cuối khi xuất ra lại hiện kết quả được gán trong khối lệnh con. Mình giá trị của biến local_variable1 được gán trong khối lệnh con sẽ chỉ có ý nghĩa trong khối lệnh con. Ở bên ngoài sẽ lấy giá trị được khai báo phía trên. Ai giải thích hộ mình với?

Cái này bạn hiểu sai :smiley: phải là: biến khai báo trong một khối lệnh chỉ tồn tại trong khối lệnh đó. Để ý hình số 7 là lỗi biên dịch.

2 Likes

Cám ơn bạn. Biến khai báo trong một khối lệnh chỉ tồn tại trong khối lệnh đó. Mình cũng xem hình 7, nhưng trong phần code của mình giá trị của local_variable1 lại xuất ra giá trị được gán trong khối lệnh đó 5.3 mà không phải giá trị được khai báo ban đầu 1.2, chỗ này mình vẫn chưa hiểu.

Thì nó nằm trong {} của main nên nó có hiệu lực tại mọi chỗ nằm trong {} của main và phía sau vị trí khai báo.
Tất nhiên nó có hiệu lực trong trong cả cái {} con nằm phía trong {} của main.

Hiểu là có hiệu lực truy cập - tức là truy cập được.

2 Likes

Mọi người hình như chưa hiểu ý mình. Vì sao local_variable1 được khai báo trước và nhận giá trị 1.2, sau đó trong khối lệnh con mình gán giá trị 5.3 cho local_variable1. Khi kết thúc khối lệnh con, khi xuất dữ liệu của local_variable1 lại nhận được giá trị 5.3 chứ không phải 1.2? (Theo lý thuyết khi kết thúc khối lệnh con thì biến sẽ bị tiêu hủy, local_variable1 sẽ nhận lại giá trị ban đầu ngoài khối lệnh con)

Cái lý thuyết ra khỏi hàm mà biến ngoài hàm bị reset về giá trị trước đó là ai dạy bạn vậy ?
Khi nói đến phạm vi người chỉ hiểu là phạm vi mà có thể sử dụng (gán vào , lấy ra) của biến. Ngoài ra không còn gì hơn.

4 Likes

Trong khối lệnh con, bạn khai báo là đc. vd : int local_variable1; là bạn thay đổi trong khối lệnh con sẽ ko ảnh hưởng tới biến ở khối lệnh ngoài. Bạn là vì ko khai báo nên compiler nó sẽ lấy biến ở khối lệnh ngoài và thực hiện mọi thay đổi trên đó

3 Likes

Cám ơn bạn mình đã hiểu, do trong khối lệnh con mình không khai báo mình chỉ gán, nếu mình khai báo thì không có vấn đề gì.

Ok bạn. Có bạn bên dưới comment câu trả lời đúng câu hỏi của mình rồi.

Thế là bên ngoài scope có biến local_variable1, trong scope cũng khai báo biến local_variable1 khác ?
Oắt đờ phước @@.
Khi bạn đang hiểu sai vấn đề và cố gắng biến nó theo ý hiểu của mình ?

3 Likes

Trong bài hướng dẫn có nói về cái này và không khuyến khích ghi như vậy. Chỉ là đang lấy ví dụ để hiểu hơn, bạn không cần căng thẳng vậy đâu. :grinning::grinning::grinning:

Chúc bạn học tốt…

1 Like

Đó là chuyện bình thường mà b, biến cục bộ trùng tên với biến global thoải mái mà b.

Tất nhiên, các trình biên dịch hiện tại có thể dễ tính và đủ thông minh để hiểu 2 thằng đó khác nhau.
Nhưng nó vẫn có sự khác biệt. Khác biệt kiểu vậy :
IMG_6297

3 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?