[Khóa học C++] Bài 4 - A first look at variables, initialization, and assignment

Share:
Trước khi đi vào các phần cụ thể trong C++, trong bài này, bạn sẽ có cái nhìn đầu tiên về biến initialization và assignment. Đây tiếp tục là những khái niệm cơ bản trong C++, tuy nhiên, bạn hãy nắm vững những khái niệm này để sau này, chúng ta cùng nghiên cứu thêm các vấn đề phức tạp hơn về bộ nhớ.


Objects

C++ (hay các ngôn ngữ lập trình khác) tạo ra các object, truy cập, sử dụng chúng. Bạn có thể hiểu đơn giản, một object là một vùng nhớ mà máy tính cấp cho chúng ta để lưu trữ dữ liệu. Và máy tính sẽ sử dụng RAM (random access memory) để cho chương trình của chúng ta sử dụng. Trong C++, một object có thể là một biến (variable), lớp (class), struct (kiểu dữ liệu do người dùng tự định nghĩa).

Khi bạn tạo một object, máy tính sẽ cấp cho bạn một vùng nhớ để lưu trữ dữ liệu của object đó. Bạn sẽ được tìm hiểu sâu vào bộ nhớ trong các bài sau.


Variables

Chúng ta có một lệnh gán x = 5; và bạn có thể dể dàng đoán ra rằng, giá trị 5 sẽ được lưu vào x. Vậy chính xác, x là một biến (variable).
Và trong bài này, chúng ta đơn thuần tìm hiểu về các biến số nguyên (integer). Khái niệm cơ bản sau, một biến nguyên là biến lưu trữ các giá trị nguyên.

Để tạo ra một biến, bạn sẽ dùng một câu lệnh khởi tạo đặc biệt (definition) để tạo ra một biến. Ví dụ int x; chính là một lệnh defination. Bạn hãy lưu ý 2 khái niệm, declaration và defination, chúng ta sẽ tìm hiểu sự khác biệt của nó sau.

Khi câu lệnh trên được thực thi bởi CPU, một vùng nhớ sẽ được dành ra cho biến x (lúc này biến x chưa có giá trị, và nó sẽ mang một giá trị rác), hành động này gọi là instantiation. Và khi bất kì object nào được tạo ra với một vùng nhớ được máy tính cấp, vùng nhớ sẽ luôn có một địa chỉ (address). Bạn sẽ tìm hiểu sau trong phần con trỏ (pointer).

l-values and r-values

Trong C++, biến là một loại của I-value. Một I-value là một giá trị mà có một địa chỉ cụ thể (trong bộ nhớ), cho nên tất cả các biến là I-value, vì tất cả các biến đều được cấp một địa chỉ cụ thể trong bộ nhớ như đã tìm hiểu ở trên. 

Đối ngược với I-value là r-value, một r-value là một giá trị mà không có địa chỉ trong bộ nhớ.
Trên đây là 2 khái niệm khá hay và khá lạ đối với các bạn. Và để hiểu rõ hơn về 2 khái niệm này chúng ta cùng xem ví dụ về một lệnh gán.

y = 4; trong lệnh gán này, bên trái biến y chính là I-value, bên phải số 4 chính là r-value.
Tóm lại thì Ivalue sẽ nằm bên trái câu lệnh gán, và r-value sẽ nằm bên phải câu lệnh gán. Do đó một lệnh kiểu như 5 = 6; sẽ gây ra lỗi, vì bên trái nó không phải là I-value.

Sau đây là một vài câu lệnh gán, bạn sẽ hiểu thêm về I-value và r-value.
Có một lưu ý rằng, trong code, chúng tôi sẽ sử dụng tiếng Anh, bạn nên luyện tiếng Anh tốt hơn để có một sự nghiệp tốt đẹp.
Qua trở lại ví dụ trên, tại dòng số 8, x được sử dụng trong 2 bối cảnh: bên trái, x là I-value vì x là một biến có giá trị và để lưu trữ một giá trị. Bên phải, x vẫn là một biến, nhưng x không dùng để lưu giá trị, mà x được hiểu là một giá trị (trong trường hợp này là 7). Phần bên phải chính là một r-value. Vì thế câu lệnh này sẽ tương đương với x = 7 + 1; hay x = 8;

Bạn đừng lo lắng nếu vẫn còn nhiều thắc mắc về I-value và r-value, bạn có thể quay lại bài viết trong tương lai.

Initialization vs. assignment

C++ cung cấp 2 khái niệm là: Initializationvà assignment.

Sau khi một biến được tạo ra, một giá trị sẽ được gán (assigned) thông qua phép gán (dấu =) như sau:


C++ cho phép bạn vừa thực hiện việc tạo ra một biến và gán giá trị cho biến luôn như dòng số 4. Và điều này gọi là initialization.
Tóm lại dòng số 2 chính là assignment và dòng số 4 chính là initialization.

Cả 2 khái niệm này khá là tự nhiên, nhưng chúng ta cần phân biệt rõ ràng 2 khái niệm này, vì sau này một số kiểu dữ liệu cần được khởi tạo giá trị ngay khi được tạo ra.

Rule: Khi khởi tạo một biến, hãy dùng initialization.

Uninitialized variables

Không giống như các ngôn ngữ khác, C/C++ sẽ không tạo ra các giá trị mặc định (như là 0) cho các biến không được khởi tạo giá trị ban đầu. Vì vậy nó sẽ mang giá trị rác nếu không được khởi tạo (như dòng đầu tiên trong đoạn code trên). Biến như thế này gọi là uninitialized variable.

Trong trường hợp này, biến x trên không được khởi tạo giá trị ban đầu, và như trên thì biến x sẽ mang một giá trị rác, và bạn hãy thử in giá trị x ra màn hình. Lần 1 nó có thể là 7177728, lần biên dịch thứ 2 có thể là 5277592 chẳng hạn. Bạn không thể biết được giá trị của x là bao nhiêu sau mỗi lần chạy.

Việc sử dụng một biến mà không được khởi tạo giá trị là cực kì nguy hiểm và là một lỗi lớn của lập trình viên, đôi lúc nó có thể gây ra một bug rất lớn. Và trong một số trình biên dịch hiện nay như Visual Studio, bạn sẽ nhận được một cảnh báo (warning) nếu bạn sử dụng biến mà không khởi tạo giá trị.

Undefined behavior

Trường hợp trên chính là một ví dụ của undefined behavior (hành vi không được định nghĩa bởi ngôn ngữ lập trình). Và khi một đoạn code là undefined behavior thì chúng ta không thể kiểm soát được kết quả của chương trình.

Một số dấu hiệu của undefined behavior như sau:

- Chương trình của bạn không có một kết quả nhất quán, ví dụ trong trường hợp phía trên, lúc thì in ra màn hình giá trị này, lúc thì in ra màn hình giá trị kia. Lúc chạy đúng, lúc chạy sai.
- Chương trình thì có vẻ như là đúng, nhưng kết quả lại không được như mong đơi hoặc sai.
- Chương trình của bạn bị crash (bị treo).
- Chương trình của bạn cho kết quả khác nhau ở các compiler.

Rule: Tránh các trường hợp undefined behavior.

Kết thúc!



2 nhận xét: