Wednesday, April 22, 2015

Hiểu về Union [Understand about Union]

Union là gì?

Để đảm bảo an toàn dữ liệu, mỗi biến sau khi khai báo sẽ được cấp phát một vùng nhớ tách biệt với nhau, nhưng vì nhu cầu giải quyết các bài toán đa dạng, đôi lúc bạn sẽ muốn khai báo tất cả chúng "đè" lên nhau.
Union chính là cấu trúc hỗ trợ bạn thực hiện điều đó, bạn có thể khai báo nhiều biến với nhiều kiểu dữ liệu khác nhau trên cùng một vùng nhớ.

Khai báo Union như thế nào?

Khai báo union với cấu trúc như sau:
Hình 1: Cấu trúc khai báo kiểu union
Trong đó union là từ khóa bắt buộc, union tạo ra một kiểu dữ liệu riêng, và các biến thành phần cũng được truy xuất bằng giấu '.' tương tự như struct.
Hình 2: Khai báo và truy xuất giá trị của union
Trong Hình 2 chúng ta đã khai báo một kiểu union có tên là key, và trong hàm main khai báo biến My_key có kiểu dữ liệu là key, khởi tạo và truy xuất giá trị cho các biến thành phần của My_key.

Ghi "đè" nghĩa là sao?

Để xác minh việc union ghi "đè" các biến thành phần lên nhau ta sẽ chạy đoạn chương trình như sau:
Hình 3: đoạn chương trình kiểm tra kích thước của union
Kết quả là: 
Hình 4: Kết quả đoạn chương trình kiểm tra kích thước union
Ta thấy: Kích thước của key_name là 1 byte, key_number là 4 byte. 
Nếu key_name và key_number nằm ở hai vùng nhớ khác nhau thì union key phải có kích thước 5 byte mới chứa đủ 2 biến thành phần này.
Nhưng: Tổng kích thước của union không phải là 5 byte mà là 4byte, điều đó có nghĩa là key_name và key_number được ghi "đè" lên nhau chứ không phải tách rời nhau.

Tác dụng của việc ghi "đè"?

Bây giờ hãy quay lại ví dụ về union ở Hình 2, chạy đoạn chương trình đó và kết quả như sau:
Hình 3: Kết quả chạy chương trình Hình 2
Kết quả chỉ có giá trị của key_name là giống với giá trị khởi tạo ban đầu còn key_number thì không.
Câu hỏi: Vì sao kết quả chạy chương trình lại như thế?
Trả lời: Trong union biến thành phần nào được gán giá trị sau sẽ ghi đè lên những biến thành phần còn lại.

Như vậy, theo thứ tự câu lệnh trong đoạn chương trình Hình 2, ta thấy rằng biến key_number được gán giá trị trước, key_name được gán sau, do đó giá trị của key_name sẽ ghi đè lên giá trị của key_number và làm sai khác giá trị trước đó của key_number, dẫn tới kết quả chạy chương trình là key_name đúng, key_number sai.

Cơ chế ghi "đè" của union?

Nhưng: Chú ý kỹ hơn nữa ta sẽ thấy 86 chính là giá trị thập phân của ký tự in hoa 'V', vậy có mối liên hệ gì ở đây?
Để hiểu rõ vấn đề này chúng ta phải hiểu cách union "ghi đè" các biến lên bộ nhớ như thế nào
Hình 4: Vùng nhớ 4byte của union key
Khi thực hiện câu lệnh My_key.key_number = 19; giá tri hexa tương ứng 0x00000014 sẽ được ghi vào 4 byte nhớ, byte 0 có giá trị 0x14 và các byte còn lại có giá trị 0x00.
Đến câu lệnh My_key.key_name = 'V'; giá trị hexa 0x56 sẽ được ghi vào byte 0 và làm mất giá trị 0x14 trước đó.
Do đó khi truy xuất My_key.key_number giá trị đọc được sẽ là 0x00000056 = 86 chứ không phải là 0x00000014 = 19 như ban đầu nữa, điều này giải thích cho kết quả chương trình.

Vậy: Union sẽ ghi giá trị các biến thành phần vào các vị trí ô nhớ của nó từ byte thấp đến byte cao, biến ghi sau sẽ đè lên biến ghi trước, key_name chỉ có dung lượng 1 byte nên giá trị của nó sẽ được ghi vào byte 0, key_number có dung lượng 4 byte do đó giá trị của nó sẽ được ghi vào cả 4 ô nhớ theo thứ tự từ byte thấp (byte 0) đến byte cao (byte 4).

Hôm nay vậy là đủ, chúc các bạn buổi tối vui vẻ ^^, hẹn gặp lại trong các bài viết tiếp theo.


No comments:

Post a Comment