Struct và Union trong C: Cách tổ chức dữ liệu phức tạp hiệu quả

banner

Tóm tắt kiến thức

Struct và Union là công cụ mạnh mẽ để tạo ra các kiểu dữ liệu tùy chỉnh trong C. Struct giúp tổ chức dữ liệu có cấu trúc, trong khi Union tối ưu bộ nhớ bằng cách chia sẻ không gian lưu trữ. Hiểu rõ cả hai là bước quan trọng để xây dựng ứng dụng phức tạp.

Struct và Union là hai kiểu dữ liệu do người dùng định nghĩa (user-defined data types) trong C, cho phép nhóm nhiều biến có kiểu dữ liệu khác nhau lại với nhau. Chúng giúp tổ chức dữ liệu một cách có cấu trúc, dễ quản lý và tối ưu hiệu suất.

Quảng cáo giúp chúng tôi duy trì trang web này

Tổng quan về Struct

Khái niệm Struct

Struct (cấu trúc) là một kiểu dữ liệu cho phép nhóm nhiều biến có kiểu dữ liệu khác nhau dưới một tên duy nhất. Mỗi biến trong struct được gọi là thành phần (member).

Cú pháp định nghĩa Struct

Cú pháp struct
struct tên_struct {
    kiểu_dữ_liệu thành_phần_1;
    kiểu_dữ_liệu thành_phần_2;
    // ... các thành phần khác
};

Ví dụ thực tế:

#include <stdio.h>

// Định nghĩa struct
struct Student {
    int id;
    char name[50];
    float grade;
    int age;
};

int main() {
    // Khai báo biến struct
    struct Student student1;

    // Gán giá trị cho các thành phần
    student1.id = 1001;
    strcpy(student1.name, "Nguyen Van A");
    student1.grade = 8.5;
    student1.age = 20;

    // In thông tin
    printf("ID: %d\n", student1.id);
    printf("Ten: %s\n", student1.name);
    printf("Diem: %.2f\n", student1.grade);
    printf("Tuoi: %d\n", student1.age);

    return 0;
}

Khởi tạo Struct

Khởi tạo khi khai báo

#include <stdio.h>
#include <string.h>

struct Student {
    int id;
    char name[50];
    float grade;
    int age;
};

int main() {
    // Khởi tạo với giá trị
    struct Student student1 = {1001, "Nguyen Van A", 8.5, 20};

    // Khởi tạo một phần
    struct Student student2 = {1002, "Tran Thi B"};

    // Khởi tạo với danh sách khởi tạo (initializer list)
    struct Student student3 = {
        .id = 1003,
        .name = "Le Van C",
        .grade = 9.0,
        .age = 21
    };

    printf("Student 1: %s, Grade: %.2f\n", student1.name, student1.grade);
    printf("Student 2: %s, Age: %d\n", student2.name, student2.age);
    printf("Student 3: %s, Grade: %.2f\n", student3.name, student3.grade);

    return 0;
}

Truy cập thành phần Struct

Sử dụng toán tử dấu chấm (.)

#include <stdio.h>
#include <string.h>

struct Point {
    int x;
    int y;
};

struct Rectangle {
    struct Point topLeft;
    struct Point bottomRight;
};

int main() {
    struct Point p1 = {10, 20};
    struct Point p2 = {30, 40};

    struct Rectangle rect = {p1, p2};

    printf("Diem 1: (%d, %d)\n", p1.x, p1.y);
    printf("Diem 2: (%d, %d)\n", p2.x, p2.y);

    printf("Hinh chu nhat:\n");
    printf("Goc trai tren: (%d, %d)\n", rect.topLeft.x, rect.topLeft.y);
    printf("Goc phai duoi: (%d, %d)\n", rect.bottomRight.x, rect.bottomRight.y);

    return 0;
}

Typedef với Struct

Sử dụng typedef để tạo alias

#include <stdio.h>
#include <string.h>

// Định nghĩa struct với typedef
typedef struct {
    int id;
    char name[50];
    float salary;
    char department[30];
} Employee;

int main() {
    Employee emp1 = {1001, "Nguyen Van A", 15000000, "IT"};
    Employee emp2;

    // Gán giá trị
    emp2.id = 1002;
    strcpy(emp2.name, "Tran Thi B");
    emp2.salary = 12000000;
    strcpy(emp2.department, "HR");

    printf("Nhan vien 1: %s, Luong: %.0f\n", emp1.name, emp1.salary);
    printf("Nhan vien 2: %s, Phong ban: %s\n", emp2.name, emp2.department);

    return 0;
}

Struct lồng nhau (Nested Structures)

#include <stdio.h>
#include <string.h>

typedef struct {
    int day;
    int month;
    int year;
} Date;

typedef struct {
    char name[50];
    Date birthDate;
    float grade;
} Student;

int main() {
    Student student1;

    strcpy(student1.name, "Nguyen Van A");
    student1.birthDate.day = 15;
    student1.birthDate.month = 6;
    student1.birthDate.year = 2000;
    student1.grade = 8.5;

    printf("Ten: %s\n", student1.name);
    printf("Ngay sinh: %d/%d/%d\n",
           student1.birthDate.day,
           student1.birthDate.month,
           student1.birthDate.year);
    printf("Diem: %.2f\n", student1.grade);

    return 0;
}

Struct và hàm

Truyền struct vào hàm

#include <stdio.h>
#include <string.h>

typedef struct {
    char title[100];
    char author[50];
    int pages;
    float price;
} Book;

// Hàm nhập thông tin sách
void inputBook(Book *book) {
    printf("Nhap ten sach: ");
    fgets(book->title, sizeof(book->title), stdin);
    book->title[strcspn(book->title, "\n")] = 0;

    printf("Nhap tac gia: ");
    fgets(book->author, sizeof(book->author), stdin);
    book->author[strcspn(book->author, "\n")] = 0;

    printf("Nhap so trang: ");
    scanf("%d", &book->pages);

    printf("Nhap gia: ");
    scanf("%f", &book->price);
}

// Hàm hiển thị thông tin sách
void displayBook(Book book) {
    printf("\n=== THONG TIN SACH ===\n");
    printf("Ten sach: %s\n", book.title);
    printf("Tac gia: %s\n", book.author);
    printf("So trang: %d\n", book.pages);
    printf("Gia: %.0f VND\n", book.price);
}

int main() {
    Book book1;

    inputBook(&book1);
    displayBook(book1);

    return 0;
}

Trả về struct từ hàm

#include <stdio.h>
#include <string.h>

typedef struct {
    float x;
    float y;
} Point;

Point createPoint(float x, float y) {
    Point p;
    p.x = x;
    p.y = y;
    return p;
}

Point addPoints(Point p1, Point p2) {
    Point result;
    result.x = p1.x + p2.x;
    result.y = p1.y + p2.y;
    return result;
}

int main() {
    Point p1 = createPoint(1.5, 2.5);
    Point p2 = createPoint(3.0, 4.0);
    Point sum = addPoints(p1, p2);

    printf("Diem 1: (%.2f, %.2f)\n", p1.x, p1.y);
    printf("Diem 2: (%.2f, %.2f)\n", p2.x, p2.y);
    printf("Tong: (%.2f, %.2f)\n", sum.x, sum.y);

    return 0;
}

Mảng Struct

#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[50];
    float grade;
} Student;

void inputStudents(Student students[], int n) {
    for (int i = 0; i < n; i++) {
        printf("\nNhap thong tin sinh vien %d:\n", i + 1);
        printf("ID: ");
        scanf("%d", &students[i].id);

        printf("Ten: ");
        scanf(" %[^\n]", students[i].name);

        printf("Diem: ");
        scanf("%f", &students[i].grade);
    }
}

void displayStudents(Student students[], int n) {
    printf("\n=== DANH SACH SINH VIEN ===\n");
    for (int i = 0; i < n; i++) {
        printf("ID: %d, Ten: %s, Diem: %.2f\n",
               students[i].id, students[i].name, students[i].grade);
    }
}

float calculateAverageGrade(Student students[], int n) {
    float sum = 0;
    for (int i = 0; i < n; i++) {
        sum += students[i].grade;
    }
    return sum / n;
}

int main() {
    Student students[3];

    inputStudents(students, 3);
    displayStudents(students, 3);

    float average = calculateAverageGrade(students, 3);
    printf("\nDiem trung binh: %.2f\n", average);

    return 0;
}

Union

Khái niệm Union

Union tương tự như struct, nhưng tất cả các thành phần chia sẻ cùng một vùng nhớ. Chỉ có thể sử dụng một thành phần tại một thời điểm.

Định nghĩa Union

union tên_union {
    kiểu_dữ_liệu thành_phần_1;
    kiểu_dữ_liệu thành_phần_2;
    // ... các thành phần khác
};

Ví dụ Union:

#include <stdio.h>

union Data {
    int intValue;
    float floatValue;
    char stringValue[20];
};

int main() {
    union Data data;

    // Sử dụng intValue
    data.intValue = 100;
    printf("Int value: %d\n", data.intValue);

    // Sử dụng floatValue
    data.floatValue = 3.14;
    printf("Float value: %.2f\n", data.floatValue);

    // Sử dụng stringValue
    strcpy(data.stringValue, "Hello");
    printf("String value: %s\n", data.stringValue);

    printf("Size of union: %zu bytes\n", sizeof(union Data));

    return 0;
}

So sánh Struct và Union

#include <stdio.h>
#include <string.h>

struct StructData {
    int intValue;
    float floatValue;
    char stringValue[20];
};

union UnionData {
    int intValue;
    float floatValue;
    char stringValue[20];
};

int main() {
    printf("Size of struct: %zu bytes\n", sizeof(struct StructData));
    printf("Size of union: %zu bytes\n", sizeof(union UnionData));

    struct StructData s;
    union UnionData u;

    // Struct - tất cả thành phần có thể sử dụng đồng thời
    s.intValue = 100;
    s.floatValue = 3.14;
    strcpy(s.stringValue, "Hello");

    printf("\nStruct values:\n");
    printf("Int: %d\n", s.intValue);
    printf("Float: %.2f\n", s.floatValue);
    printf("String: %s\n", s.stringValue);

    // Union - chỉ một thành phần tại một thời điểm
    u.intValue = 100;
    printf("\nUnion int value: %d\n", u.intValue);

    u.floatValue = 3.14;
    printf("Union float value: %.2f\n", u.floatValue);
    printf("Union int value after float: %d\n", u.intValue); // Giá trị đã thay đổi!

    return 0;
}

Con trỏ Struct

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct {
    char name[50];
    int age;
    float salary;
} Employee;

void inputEmployee(Employee *emp) {
    printf("Nhap ten: ");
    fgets(emp->name, sizeof(emp->name), stdin);
    emp->name[strcspn(emp->name, "\n")] = 0;

    printf("Nhap tuoi: ");
    scanf("%d", &emp->age);

    printf("Nhap luong: ");
    scanf("%f", &emp->salary);
}

void displayEmployee(Employee *emp) {
    printf("Ten: %s\n", emp->name);
    printf("Tuoi: %d\n", emp->age);
    printf("Luong: %.0f VND\n", emp->salary);
}

int main() {
    Employee *emp = (Employee*)malloc(sizeof(Employee));

    if (emp == NULL) {
        printf("Khong the cap phat bo nho!\n");
        return 1;
    }

    inputEmployee(emp);
    displayEmployee(emp);

    free(emp);
    return 0;
}

Ví dụ thực hành

1. Quản lý thông tin sách

#include <stdio.h>
#include <string.h>

typedef struct {
    char title[100];
    char author[50];
    char isbn[20];
    int pages;
    float price;
    int year;
} Book;

void inputBook(Book *book) {
    printf("Nhap ten sach: ");
    fgets(book->title, sizeof(book->title), stdin);
    book->title[strcspn(book->title, "\n")] = 0;

    printf("Nhap tac gia: ");
    fgets(book->author, sizeof(book->author), stdin);
    book->author[strcspn(book->author, "\n")] = 0;

    printf("Nhap ISBN: ");
    fgets(book->isbn, sizeof(book->isbn), stdin);
    book->isbn[strcspn(book->isbn, "\n")] = 0;

    printf("Nhap so trang: ");
    scanf("%d", &book->pages);

    printf("Nhap gia: ");
    scanf("%f", &book->price);

    printf("Nhap nam xuat ban: ");
    scanf("%d", &book->year);
}

void displayBook(Book book) {
    printf("\n=== THONG TIN SACH ===\n");
    printf("Ten sach: %s\n", book.title);
    printf("Tac gia: %s\n", book.author);
    printf("ISBN: %s\n", book.isbn);
    printf("So trang: %d\n", book.pages);
    printf("Gia: %.0f VND\n", book.price);
    printf("Nam xuat ban: %d\n", book.year);
}

int main() {
    Book books[3];

    for (int i = 0; i < 3; i++) {
        printf("\nNhap thong tin sach %d:\n", i + 1);
        inputBook(&books[i]);
    }

    printf("\n=== DANH SACH SACH ===\n");
    for (int i = 0; i < 3; i++) {
        displayBook(books[i]);
    }

    return 0;
}

2. Quản lý điểm số sinh viên

#include <stdio.h>

typedef struct {
    float math;
    float physics;
    float chemistry;
    float english;
} Grades;

typedef struct {
    char name[50];
    int id;
    Grades grades;
    float average;
} Student;

void inputStudent(Student *student) {
    printf("Nhap ten sinh vien: ");
    fgets(student->name, sizeof(student->name), stdin);
    student->name[strcspn(student->name, "\n")] = 0;

    printf("Nhap ma sinh vien: ");
    scanf("%d", &student->id);

    printf("Nhap diem toan: ");
    scanf("%f", &student->grades.math);

    printf("Nhap diem ly: ");
    scanf("%f", &student->grades.physics);

    printf("Nhap diem hoa: ");
    scanf("%f", &student->grades.chemistry);

    printf("Nhap diem anh: ");
    scanf("%f", &student->grades.english);
}

void calculateAverage(Student *student) {
    student->average = (student->grades.math +
                       student->grades.physics +
                       student->grades.chemistry +
                       student->grades.english) / 4.0;
}

void displayStudent(Student student) {
    printf("Ten: %s\n", student.name);
    printf("Ma SV: %d\n", student.id);
    printf("Diem toan: %.2f\n", student.grades.math);
    printf("Diem ly: %.2f\n", student.grades.physics);
    printf("Diem hoa: %.2f\n", student.grades.chemistry);
    printf("Diem anh: %.2f\n", student.grades.english);
    printf("Diem trung binh: %.2f\n", student.average);
}

int main() {
    Student student;

    inputStudent(&student);
    calculateAverage(&student);

    printf("\n=== THONG TIN SINH VIEN ===\n");
    displayStudent(student);

    return 0;
}

Tổng kết

Struct và Union là những công cụ mạnh mẽ để tổ chức dữ liệu phức tạp và tối ưu bộ nhớ trong C.

Lưu ý quan trọng
  • Struct: Tổng kích thước = tổng kích thước các thành phần + padding
  • Union: Kích thước = kích thước thành phần lớn nhất
  • Memory alignment: Cẩn thận với padding khi làm việc với binary data
  • Union safety: Chỉ truy cập thành phần cuối cùng được gán giá trị
Best Practices
  • Sử dụng typedef để tạo tên ngắn gọn cho struct
  • Nhóm các thành phần liên quan trong struct
  • Sử dụng union khi cần tiết kiệm bộ nhớ
  • Cẩn thận với con trỏ trong struct (cần giải phóng bộ nhớ)

Với những kiến thức này, bạn đã sẵn sàng để xây dựng các kiểu dữ liệu phức tạp và tiếp tục khám phá các cấu trúc dữ liệu nâng cao như linked list, tree, graph!

Last updated on