Union trong C là gì? Cách ứng dụng để chia nhỏ dữ liệu

Union trong C là một kiểu dữ liệu đặc biệt, chúng giúp lưu trữ nhiều kiểu dữ liệu khác nhau tại cùng một vị trí. Hiện nay, cấu trúc Union này thường được dùng để xử lý các dữ liệu chuỗi, lập trình nhúng,… Trong bài viết này, mình sẽ giải đáp kỹ hơn về khái niệm Union cho bạn nhé!

Union trong C là gì?

Unino có cấu trúc gồm tất cả các thành phần có chung một vị trí nhớ, và kích thước của thành phần lớn nhất trong đó. Cú pháp của Uninon khá tương tự với Struct, đều được định nghĩa bên ngoài main, nhưng chúng thay thế Struct thành Unino, như bên dưới:

union Sample {
    char a;
    float b;
    int c;
};

Bạn có thể xem hình sau để hiểu hơn về sự khác biệt giữa cấu trúc union và struct:

Sự khác biệt giữa struct và union trong C

Sau khi được khai báo, chúng ta có thể tạo và sử dụng những phiên bản Union trong các hàm khác nhau tùy theo nhu cầu, dựa trên hàm sau:

int main () {
    union Sample u_name;
}

Để khai báo một instant của union, chúng ta sẽ bắt đầu với cụm từ union và kèm theo đó là tên của union mà bạn muốn sử dụng (trong trường hợp này mình lấy thử một mẫu là Sample). Cuối cùng, bạn cần điền tên instant của union mà bạn khởi tạo (mình đang dùng u_name):

Lưu ý: Việc đặt tên cho các phiên bản này cũng tương tự như khi chúng ta tạo một biến trong chương trình vậy. Bạn có thể hình dung đơn giản là mình đang tạo một biến thuộc thể loại union Sample.

Sau khi đã có u_name, mình có thể dùng đến toán tử . để truy cập vào các biến bên trong Sample, cụ thể:

int main() {
    union Sample u_name;
    u_name.a = 'c';
    u_name.b = 87.99;
    u_name.c = 103;
}

Tính đến thời điểm mình viết bài blog này, cách sử dụng union trong C cũng khá tương tự như khi sử dụng struct. Tuy nhiên, điểm khác biệt chính là tất cả các biến được tạo bên trong Smaple của union đều được lưu tại cùng một vị trí bộ nhớ.

Cả 3 u_name.a, u_name.b u_name.c đều chia sẻ một không gian bộ nhớ chung. Điều này đồng nghĩa với việc chúng ta chỉ có thể sử dụng một biến tại một thời điểm nhất định.

Cụ thể, trongg ví dụ về cách tạo union trong C trên, khi mình tạo u_name.ac, tất cả các biến đều trỏ đến cùng một không gian bộ nhớ, nên chúng có giá trị tương đương nhau.

Tương tự, với đoạn code tiếp theo, mình gán giá trị 87.99 cho u_name.b, nhưng trên thực tế thì giá trị này cũng sẽ cập nhật cho tất cả các biến khác.

Lưu ý khi dùng union

Theo mình thấy thì việc đồng nhất giá trị trong các biến của cấu trúc Union khá hữu ích khi cần phải lưu trữ một dữ liệu mà chính nó là một trong nhiều kiểu dữ liệu đã được lưu trong union. Một union cho thể chứa đa dạng các kiểu dữ liệu khác nhau khi được yêu cầu.

Tuy nhiên, bạn cũng cần lưu ý rằng chúng ta không nên sử dụng tất cả các biến trong cấu trúc union của C. Giả sử như chúng ta đang có kiểu dữ liệu là char. Trong ví dụ trên, mình đã sử dụng biến a để gán giá trị vì nó là một loại kiểu dữ liệu char. Lúc đó, mỗi một biến a là một giá trị, còn biến b c sẽ chứa các giá trị rác và chúng ta không nên sử dụng.

Code mẫu về union trong C

Bạn có thể tham khảo chương trình sau:

#include <iostream>
using namespace std;

// creating union
union Sample {
  char a; 
  float b;
  int c;
};

int main() {
  // initialising a union
  union Sample u_name;

  // assigning value to a
  u_name.a = 'c'; 

  // printing all values
  cout << "a " << u_name.a << endl;
  cout << "b " << u_name.b << endl;
  cout << "c " << u_name.c << endl;
  cout << endl << endl;

  // assigning value to b
  u_name.b = 87.99;

  // printing all values
  cout << "a " << u_name.a << endl;
  cout << "b " << u_name.b << endl;
  cout << "c " << u_name.c << endl;
  cout << endl << endl;

  // assigning value to c
  u_name.c = 103;

  // printing all values
  cout << "a " << u_name.a << endl;
  cout << "b " << u_name.b << endl;
  cout << "c " << u_name.c << endl;
  cout << endl << endl;

  return 0;
}

Trong đoạn code trên, bạn có thể thấy là khi chúng ta gán giá trị cho biến a, b hoặc c bất kỳ, thì 2 biến còn lại sẽ tự động cập nhật thành một giá trị ngẫu nhiên nào đó. Chúng ta cũng có thể gọi đó là giá trị rác.

Thông thường thì hai loại biến còn lại này sẽ chứa cùng một giá trị và chuyển đổi thành kiểu giá trị của riêng mình. Bạn không nên sử dụng các giá trị rác này.

Các trường hợp cần dùng đến Union

Vậy, các trường hợp cần dùng đến Union trong C là gì? Dưới đây là một số trường hợp chính:

Cần tiết kiệm bộ nhớ

Trong cùng 1 không gian, 1 biến được khởi tạo khi cần dùng (bằng cách gọi hàm) và chúng cũng biến mất khi hàm kết thúc. Hãy xem qua một đoạn code mẫu sau:

void func()
{
	// (1) Đoạn code cần dùng biến ram
	int ram = 2048; // MB
	
	printf("RAM: %d", ram);

	// (2) Đoạn code không cần dùng biến ram, nhưng cần dùng biến hdd
	int ssd = 1024; // GB
	
	printf("SSD: %d", ssd);
}

Với chương trình trên, chúng ta chỉ cần dùng 8 bytes để phục vụ cho 2 biến là ram hdd. Vậy, điều này có đồng nghĩa với việc khi dùng hdd, chúng ta có thể sử dụng luôn ram để lưu trữ thông tin ssd không ? (Vì lúc này ram không cần dùng nữa).

Điều này thì sẽ đúng trên lý thuyết. Nhưng trên thực tế, nếu làm vậy thì bạn sẽ gặp nhiều khó khăn, biến ram không thể hiểu rõ và hiển thị ngữ nghĩa, gây ra các lỗi hệ thống, lỗi chương trình tiềm ẩn.

Để giải quyết vấn đề này, chúng ta sẽ dùng đến cấu trúc union. Chương trình union trong C trong trường hợp này là:

// Khai báo 1 union
union Hardware
{
	short	_ram;
	int	_ssd;
};

void func()
{
	Hardware hw;

	// (1) Đoạn code cần dùng ram
	hw.ram = 2048; // MB
	
	printf("RAM: %d", hw.ram);

	// (2) Đoạn code không cần dùng ram, nhưng cần dùng hdd
	hw.ssd = 1024; // GB

	printf("SSD: %d", hw.ssd);
}

Việc tiết kiệm bộ nhớ này sẽ rất hữu ích khi bạn dùng các mạch có ít bộ nhớ, ví dụ như Arduino UNO R3 trên ATmega328P. Theo mình nhớ thì loại mạch này chỉ có khoảng 2KB để sử dụng, khá là nhỏ.

Khi bạn cần tổ chức chương trình

Để nói rõ hơn về trường hợp này, bạn hãy xem qua đoạn code mẫu bên dưới của mình nhé:

union Vector4
{
	struct {
		float r, g, b, a;
	};
	
	struct {
		float x, y, z, w;
	};
	
	struct {
		float s, t, p, q;
	};
	
	float color[4];
	float position[4];
	float textcoord[4];
};

void rgba_vn()
{
	Vector4 logo_color;
	logo_color.r = 255.0f;
	logo_color.color[0] = 240.0f;
	
	Vector4 monster_position;
	monster_position.x = 100.0f;
	
	// ...
}

Với đoạn code trên, mình đã dùng union trong C cho 1 kiểu dữ liệu thống nhất là vector4, và lưu chúng trong 3 trường hợp khác nhau: color, posotion textcoord. Trong trường hợp cần thiết, mình cũng có thể sử dụng các chỉ số dưới dạng mảng.

Như bạn thấy, điều này giúp chương trình được tổ chức đơn giản và dễ dàng hơn.

Lời kết

Qua bài viết trên, hy vọng rằng bạn đã hiểu rõ hơn về Union trong C. Đây là kiểu dữ liệu rất hữu ích khi cần phải lưu trữ nhiều dữ liệu khác nhau trên các mạch có bộ nhớ nhỏ. Cùng theo dõi một số bài viết khác trên IoTZone trong cùng chủ đề lập trình C 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 *