Struct Trong C/C++: Khái Niệm, Ứng Dụng và Bài Tập Thực Hành

Struct (cấu trúc) là một kiểu dữ liệu phức hợp trong C/C++, cho phép bạn nhóm các biến có kiểu dữ liệu khác nhau lại với nhau dưới một tên duy nhất. Bài viết này sẽ cung cấp một cái nhìn tổng quan về struct, từ khái niệm cơ bản đến các ứng dụng nâng cao, kèm theo ví dụ minh họa và bài tập thực hành để bạn nắm vững kiến thức.

Khái Niệm Struct

Struct là một tập hợp các thành viên dữ liệu (data members), mỗi thành viên có thể có kiểu dữ liệu khác nhau. Các thành viên này được truy cập thông qua tên của struct và toán tử chấm (.). Việc sử dụng struct giúp tổ chức dữ liệu một cách logic và dễ quản lý hơn, đặc biệt khi làm việc với các đối tượng phức tạp.

Ảnh minh họa: Struct giúp tổ chức dữ liệu một cách trực quan và hiệu quả.

Khai Báo Struct

Để khai báo một struct, bạn sử dụng từ khóa struct theo cú pháp sau:

struct <tên_struct> {
    <kiểu_dữ_liệu> <tên_biến_1>;
    <kiểu_dữ_liệu> <tên_biến_2>;
    // ...
};

Ví dụ, để khai báo một struct SinhVien chứa thông tin về sinh viên, bạn có thể làm như sau:

struct SinhVien {
    int maSo;
    char hoTen[50];
    float diemTrungBinh;
};

Khởi Tạo Struct

Có nhiều cách để khởi tạo một biến struct:

  1. Khởi tạo trực tiếp:
SinhVien sv1 = {123, "Nguyen Van A", 8.5};
  1. Khởi tạo từng thành phần:
SinhVien sv2;
sv2.maSo = 456;
strcpy(sv2.hoTen, "Tran Thi B");
sv2.diemTrungBinh = 9.0;
  1. Sử dụng constructor (trong C++):
struct SinhVien {
    int maSo;
    char hoTen[50];
    float diemTrungBinh;

    SinhVien(int ms, const char* ht, float dtb) : maSo(ms), diemTrungBinh(dtb) {
        strcpy(hoTen, ht);
    }
};

SinhVien sv3(789, "Le Van C", 7.8);

Ảnh minh họa: Các cách khởi tạo struct trong C.

Truy Cập Thành Viên Struct

Để truy cập vào một thành viên của struct, bạn sử dụng toán tử chấm (.) theo cú pháp:

<tên_biến_struct>.<tên_thành_viên>

Ví dụ:

printf("Ma so: %dn", sv1.maSo);
printf("Ho ten: %sn", sv1.hoTen);
printf("Diem trung binh: %.2fn", sv1.diemTrungBinh);

Gán Struct

Bạn có thể gán giá trị của một biến struct cho một biến struct khác cùng kiểu bằng phép gán thông thường:

SinhVien sv4 = sv1; // sv4 sẽ có các giá trị giống như sv1

Sao Chép Struct với memcpy()

Trong một số trường hợp phức tạp, việc gán trực tiếp có thể không hoạt động như mong đợi, đặc biệt khi struct chứa các con trỏ. Khi đó, bạn có thể sử dụng hàm memcpy() để sao chép toàn bộ bộ nhớ của struct:

#include <string.h>

memcpy(&sv4, &sv1, sizeof(SinhVien));

Hàm memcpy() sao chép sizeof(SinhVien) byte từ địa chỉ của sv1 sang địa chỉ của sv4.

Struct Lồng Nhau

Một struct có thể chứa các struct khác như là thành viên của nó. Đây được gọi là struct lồng nhau (nested struct).

struct DiaChi {
    char duong[100];
    char quan[50];
    char thanhPho[50];
};

struct NhanVien {
    int maNV;
    char tenNV[50];
    DiaChi diaChi; // Struct DiaChi là một thành viên của struct NhanVien
};

NhanVien nv1;
strcpy(nv1.diaChi.duong, "123 Le Loi");

Ảnh minh họa: Struct lồng nhau giúp biểu diễn các mối quan hệ phức tạp giữa các đối tượng.

Mảng Struct

Bạn có thể tạo một mảng các struct, tương tự như mảng các kiểu dữ liệu khác.

SinhVien danhSachSinhVien[100]; // Mảng chứa tối đa 100 sinh viên

danhSachSinhVien[0].maSo = 1;
strcpy(danhSachSinhVien[0].hoTen, "Nguyen Van Teo");

Con Trỏ Đến Struct

Bạn có thể khai báo một con trỏ đến một struct và sử dụng toán tử mũi tên (->) để truy cập các thành viên của struct thông qua con trỏ.

SinhVien *ptr = &sv1;

printf("Ma so: %dn", ptr->maSo); // Tương đương với sv1.maSo

Từ Khóa typedef

Từ khóa typedef cho phép bạn tạo một bí danh (alias) cho một kiểu dữ liệu đã tồn tại. Điều này có thể giúp code trở nên dễ đọc và dễ bảo trì hơn.

typedef struct SinhVien SV; // Tạo bí danh SV cho struct SinhVien

SV sv5; // Tương đương với SinhVien sv5;

Các Cách Khai Báo Struct

Có hai cách khai báo struct phổ biến:

Cách 1:

struct SinhVien {
    // ...
};

Cách 2:

typedef struct {
    // ...
} SinhVien;

Cách thứ hai thường được sử dụng hơn vì nó cho phép bạn sử dụng trực tiếp tên SinhVien mà không cần từ khóa struct.

Bài Tập Thực Hành

Bài 1: Khai báo một struct HinhChuNhat để lưu trữ thông tin về chiều dài và chiều rộng của hình chữ nhật. Viết các hàm để:

  • Nhập thông tin hình chữ nhật từ người dùng.
  • Tính chu vi của hình chữ nhật.
  • Tính diện tích của hình chữ nhật.
  • Xuất thông tin hình chữ nhật ra màn hình.

Hướng dẫn:

#include <iostream>

using namespace std;

struct HinhChuNhat {
    int chieuDai;
    int chieuRong;
};

void NhapThongTinHCN(HinhChuNhat &hcn) {
    cout << "Nhap chieu dai: ";
    cin >> hcn.chieuDai;
    cout << "Nhap chieu rong: ";
    cin >> hcn.chieuRong;
}

int TinhChuViHCN(HinhChuNhat hcn) {
    return (hcn.chieuDai + hcn.chieuRong) * 2;
}

int TinhDienTichHCN(HinhChuNhat hcn) {
    return hcn.chieuDai * hcn.chieuRong;
}

void XuatThongTinHCN(HinhChuNhat hcn) {
    cout << "Chieu dai: " << hcn.chieuDai << endl;
    cout << "Chieu rong: " << hcn.chieuRong << endl;
    cout << "Chu vi: " << TinhChuViHCN(hcn) << endl;
    cout << "Dien tich: " << TinhDienTichHCN(hcn) << endl;
}

int main() {
    HinhChuNhat hcn;
    NhapThongTinHCN(hcn);
    XuatThongTinHCN(hcn);

    return 0;
}

Bài 2: Khai báo một struct PhanSo để lưu trữ thông tin về tử số và mẫu số của một phân số. Viết các hàm để:

  • Nhập thông tin phân số từ người dùng.
  • Tính tổng của hai phân số.
  • Rút gọn phân số.
  • Xuất phân số ra màn hình.

Bài 3: Khai báo một struct NhanVien để lưu trữ thông tin về nhân viên (mã nhân viên, tên nhân viên, chức vụ, lương). Viết các hàm để:

  • Nhập thông tin nhân viên.
  • Tính phụ cấp chức vụ (ví dụ: quản lý có phụ cấp cao hơn nhân viên).
  • In thông tin nhân viên, bao gồm cả phụ cấp.

Bài 4: Khai báo một struct Diem để mô tả một điểm trên mặt phẳng tọa độ Oxy. Viết các hàm để:

  • Nhập tọa độ của điểm.
  • Tính khoảng cách từ điểm đến gốc tọa độ.
  • Tính khoảng cách giữa hai điểm.

Bài 5: Khai báo một struct để lưu trữ điểm của sinh viên trong các môn học (Toán, Lý, Hóa). Viết các hàm để:

  • Nhập điểm cho sinh viên.
  • Tính điểm trung bình.
  • Xếp loại học lực (giỏi, khá, trung bình, yếu).

Kết Luận

Struct là một công cụ mạnh mẽ trong C/C++ giúp bạn tổ chức và quản lý dữ liệu một cách hiệu quả. Bằng cách nắm vững các khái niệm và kỹ thuật trình bày trong bài viết này, bạn có thể sử dụng struct để giải quyết nhiều vấn đề lập trình phức tạp. Hãy thực hành các bài tập để củng cố kiến thức và khám phá thêm các ứng dụng khác của struct trong thực tế.

Xem thêm: Mảng Struct và ví dụ ứng dụng.