1.Tại sao cần cấp phát bộ nhớ động cho mảng struct?

Như ta đã biết, mảng struct thực chất cũng là một mảng bình thường và các phần tử trong mảng đều là các struct.

Như vậy, việc khai báo và sử dụng mảng struct bình thường cũng chính là việc cấp phát các địa chỉ ô nhớ liền kề nhau. Điều đó tạo ra một vấn đề đó là đôi khi không tận dụng hết được vùng nhớ còn trống và có thể xảy ra vấn đề không đủ bộ nhớ cấp phát.

Ta biết rằng, trong mảng để xử lý được vấn đề trên ta sử dụng con trỏ kết hợp với các hàm cho phép cấp phát bộ nhớ động thì ở mảng chứa struct cũng thực hiện cấp phát bộ nhớ tương tự như mảng bình thường.

2.Cấp phát bộ nhớ động cho mảng struct bằng malloc

Để minh họa việc sử dụng hàm malloc để cấp phát động, tôi sẽ lấy ví dụ đang cấp phát bộ nhớ động cho struct sinh viên:

struct sinhvien{
    int MaSinhVien;
    char TenSinhVien[25];
    float DiemSinhVien;
};
typedef sinhvien SV;

Đầu tiên, ta khai báo mảng dưới dạng con trỏ:

SV *arr_sv;

Chú ý: Vì ở trên, khi khai báo struct ta đã đặt từ khóa typedef nên việc gọi chỉ ngắn gọn lại là SV

Tiếp theo, ta thực hiện việc cấp phát:

arr_sv = (SV*) malloc (n*sizeof(SV));

Trong đó:

  • SV là kiểu cần cấp phát
  • Malloc là hàm cấp phát (thuộc thư viện stdlib.h nên ta cần #include <stdlib.h>)
  • N là số lượng phẩn tử cần cấp phát
  • Sizeof(SV) là kích thước của kiểu SV

Để dễ hình dung hơn về việc cấp phát, tôi sẽ sử dụng việc cấp phát trên cho ví dụ nhập xuất mảng chứa struct bằng bộ nhớ động:

Thư viện để thực hiện ví dụ này bao gồm:

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

Đầu tiên, tôi khai báo struct sinh viên:

struct sinhvien{
    int MaSinhVien;
    char TenSinhVien[25];
    float DiemSinhVien;
};
typedef sinhvien SV;

Tiếp theo, ở trong hàm main tôi khai báo việc nhập N sinh viên có kiểu struct vào bộ nhớ động thông qua việc sử dụng con trỏ có kiểu SV *arr_sv;

int main(){
    int n;
    printf("Nhap N sinh vien: ");
    scanf("%d", &n);
    SV *arr_sv;
    arr_sv = (SV*) malloc (n*sizeof(SV));
}   

Hàm nhập sinh viên:

void nhap(SV *arr_sv,int n){
    for(int i = 0; i< n; i++){
        printf("\nNHAP MA SINH VIEN: ");
        scanf("%d", &(arr_sv + i)->MaSinhVien);
        printf("NHAP TEN SINH VIEN: ");
        fflush(stdin);
        gets((arr_sv + i)->TenSinhVien);
        printf("NHAP DIEM SINH VIEN: ");
        scanf("%f", &(arr_sv + i)->DiemSinhVien);
    }
}

Hàm xuất sinh viên:

void xuat(SV *arr_sv,int n){
    printf("\nTHONG TIN SINH VIEN \n");
    for(int i = 0; i< n; i++){
        printf("%d\t %s\t %f\n", (arr_sv+i)->MaSinhVien, (arr_sv+i)->TenSinhVien, (arr_sv+i)->DiemSinhVien);
    }
}

Viết lại chương trình một cách hoàn chỉnh:

#include <stdlib.h>
#include <stdio.h>
struct sinhvien{
    int MaSinhVien;
    char TenSinhVien[25];
    float DiemSinhVien;
};
typedef sinhvien SV;

void nhap(SV *arr_sv,int n){
    for(int i = 0; i< n; i++){
        printf("\nNHAP MA SINH VIEN: ");
        scanf("%d", &(arr_sv + i)->MaSinhVien);
        printf("NHAP TEN SINH VIEN: ");
        fflush(stdin);
        gets((arr_sv + i)->TenSinhVien);
        printf("NHAP DIEM SINH VIEN: ");
        scanf("%f", &(arr_sv + i)->DiemSinhVien);
    }
}

void xuat(SV *arr_sv,int n){
    printf("\nTHONG TIN SINH VIEN \n");
    for(int i = 0; i< n; i++){
        printf("%d\t %s\t %f\n", (arr_sv+i)->MaSinhVien, (arr_sv+i)->TenSinhVien, (arr_sv+i)->DiemSinhVien);
    }
}
int main(){
    int n;
    printf("Nhap N sinh vien: ");
    scanf("%d", &n);
    SV *arr_sv;
    arr_sv = (SV*) malloc (n*sizeof(SV));
    nhap(arr_sv, n);
    xuat(arr_sv, n);
}   
Nhap N sinh vien: 2

NHAP MA SINH VIEN: 1

NHAP TEN SINH VIEN: NguyenVanA

NHAP DIEM SINH VIEN: 10

NHAP MA SINH VIEN: 2

NHAP TEN SINH VIEN: NguyenVanB

NHAP DIEM SINH VIEN: 5

THONG TIN SINH VIEN

1 NguyenVanA 10.000000

2 NguyenVanB 5.000000

Thông qua ví dụ trên ta thấy rằng việc sử dụng cấp phát bộ nhớ động cho mảng struct cũng tương đương như mảng bình thường, tuy nhiên ở trong hàm nhập xuất vì ta cần nhập xuất giá trị cho các thành phần trong struct nên ta cần sử dụng toán tử mũi tên (->) để trỏ đến các thành phần đó.