Bài tập

Cấu trúc dữ liệu Struct và cách dùng Typedef trong C

Huy Erick

Trong bài viết này, chúng ta sẽ tìm hiểu về cấu trúc dữ liệu Struct và cách định nghĩa lại dữ liệu với Typedef trong lập trình C. Cấu trúc dữ liệu là gì Trong...

Trong bài viết này, chúng ta sẽ tìm hiểu về cấu trúc dữ liệu Struct và cách định nghĩa lại dữ liệu với Typedef trong lập trình C.

Cấu trúc dữ liệu là gì

Trong quá trình xây dựng ứng dụng, ngoài các kiểu dữ liệu mà ngôn ngữ lập trình C hỗ trợ, chúng ta còn có thể tự định nghĩa các kiểu dữ liệu riêng của mình. Các kiểu dữ liệu này được gọi là "kiểu dữ liệu người dùng" hoặc kiểu dữ liệu hướng người dùng.

Cấu trúc dữ liệu là một nhóm các phần tử dữ liệu được nhóm lại với nhau trong một tên. Các phần tử dữ liệu này còn được gọi là các thành viên, có thể là các kiểu dữ liệu khác nhau và độ dài khác nhau.

Ví dụ, chúng ta có thể định nghĩa kiểu dữ liệu SinhVien gồm các thông tin như Tên, ngày sinh, giới tính và số điện thoại, cân nặng... Các phần tử này có thể khác nhau về mặt dữ liệu, vì vậy không thể sử dụng một kiểu dữ liệu đơn thuần để lưu trữ được.

Khai báo dữ liệu cấu trúc Struct

Cú pháp khai báo struct:

struct [tên cấu trúc] {    định nghĩa thành viên;    định nghĩa thành viên;    ...    định nghĩa thành viên; } [một hoặc nhiều biến cấu trúc];

Ví dụ, để khai báo kiểu dữ liệu SinhVien mà không tạo ra biến nào:

struct SinhVien {    int maSV;    char ho[20];    char ten[20];    bool gioiTinh;    char queQuan[100]; };

Để tạo ra hai biến SinhVien, chúng ta có thể làm như sau:

struct SinhVien sv1, sv2;

Hoặc có thể khai báo luôn hai biến đó khi khai báo kiểu dữ liệu Struct:

struct SinhVien {    int maSV;    char ho[20];    char ten[20];    bool gioiTinh;    char queQuan[100]; } sv1, sv2;

Khởi tạo giá trị ban đầu cho biến Struct

Struct có thể được khởi tạo qua nhiều cách khác nhau, bao gồm cả việc gán các hằng số giá trị cho các thành phần. Nếu không khởi tạo giá trị, các biến có kiểu struct cũng sẽ được khởi tạo theo kiểu không tường minh.

Ví dụ với các thành phần có kiểu integer và float sẽ tự thiết lập giá trị là 0, với char và string sẽ là ''.

Cấu trúc:

struct  {    ;    ;    ...    ; }  = {giá trị 1, giá trị 2, ...};  Ví dụ:  struct SinhVien {    int maSV;    char ho[20];    char ten[20];    bool gioiTinh;    char queQuan[100];    float chieucao; } sv1 = {1, "Nguyen", "Khue", true, "ABCXYZ", 1.78};

Như trong ví dụ, trình biên dịch sẽ ngầm định giá trị phần tử từ trên xuống dưới tương ứng với giá trị từ trái sang phải. Nghĩa là maSV = 1, Họ = “Nguyen”...

Ngoài ra, chúng ta cũng có thể khởi tạo một cách tường minh hơn bằng cách gán giá trị cho từng phần tử trong struct.

Ví dụ:

struct SinhVien {    int maSV;    char ho[20];    char ten[20];    bool gioiTinh;    char queQuan[100];    float chieucao; } sv1 = {maSV: 1, ho: "Nguyen", ten: "Khue", gioiTinh: true, queQuan: "ABCXYZ", chieucao: 1.78};

Truy cập tới từng thành viên của Struct

Chúng ta có thể truy cập từng thành viên trong biến bằng toán tử ".".

Cú pháp:

.;

Ví dụ:

struct myStruct {    int a;    int b;    int c; } s1, s2;  Để truy cập chúng, ta sử dụng: s1.a; s1.b  Xét theo khía cạnh lưu trữ trên bộ nhớ (Giả sử int lưu trữ 2 byte, thực tế có thể 4 byte và a, b, c được lưu trữ tại các ô nhớ liên tiếp), khi đó hình ảnh thể hiện như sau:  ![Truy cập vào từng phần tử trong Struct](https://nanado.edu.vn/uploads/images/blog/admin/2024/06/03/cau-truc-du-lieu-struct-va-cach-dung-typedef-trong-c-1717356713.webp)  Có thể thiết lập các giá trị cho các thành viên của struct.  S1.b = 12;  Để in giá trị các thành viên thì tùy thuộc vào kiểu dữ liệu của các thành viên. Ví dụ, nếu chúng ta muốn in thông tin của thành viên b, chúng ta sử dụng câu lệnh:  ```c printf("%d", s1.b);

Các thành viên có kiểu string, kiểu mảng thì chúng ta xử lý tương tự như đã làm.

Con trỏ Struct

Một con trỏ trỏ đến cấu trúc hay biến con trỏ có kiểu cấu trúc (struct) chỉ đơn thuần là con trỏ đó trỏ đến địa chỉ của cấu trúc đó. Lưu ý con trỏ có kiểu struct không thể tự biến đổi biến con trỏ đó thành struct.

Cú pháp:

struct  /* Khai báo cấu trúc */ {    ;    ;    ...    ; } *ptr;  Để truy cập đến các thành viên của cấu trúc, chúng ta sử dụng cú pháp sau:  Cú pháp (*ptr).;  Hoặc  ptr->;

Sao chép và so sánh Struct

Một biến được khai báo có kiểu struct có thể gán cho một biến khác có cùng kiểu struct.

Ví dụ:

struct employee {    char grade;    int basic;    float allowance; };  struct employee nam = {'b', 6500, 812.5}; struct employee hung;  hung = nam; // Thực hiện phép gán biến hung = nam  Thực chất khi thực hiện phép gán hung = nam, đó chính là quá trình sao chép dữ liệu các thành viên tương ứng của nam cho hung.  Phép so sánh cũng tương tự.  ## Từ khóa typedef  Từ khóa typedef cho phép lập trình viên định nghĩa kiểu dữ liệu mới từ kiểu dữ liệu đã có trong chương trình.  Cú pháp:  ```c typedef  ;  Trong đó:  kiểu dữ liệu hiện có: Kiểu dữ liệu đã tồn tại trong chương trình kiểu dữ liệu mới: Kiểu dữ liệu mới  Ví dụ:  typedef struct {    int maSV;    char ho[20];    char ten[20];    bool gioiTinh;    char queQuan[100]; } SinhVien;  Khi khai báo, chúng ta sẽ sử dụng:  SinhVien Sv1; SinhVien Sv2;  Trong lập trình, nhiều khi chúng ta sẽ thấy người ta sử dụng kiểu dữ liệu uint8_t, uint16_t, uint32_t... đó là kiểu dữ liệu được định nghĩa từ unsigned int, unsigned long...  ## Bài tập về cấu trúc dữ liệu Struct  ### Chương trình cộng trừ nhân chia phân số trong C  ```c #include  #include  #include   int UCLN(int a, int b) {    a = abs(a);    b = abs(b);    while (a * b != 0) {       if (a > b)          a %= b;       else          b %= a;    }    return a + b; }  int BSCNN(int a, int b) {    return a * b / UCLN(a, b); }  typedef struct PhanSo {    int tuso, mauso; } PS;  PS rutGon(PS a) {    PS c;    c.tuso = a.tuso / UCLN(a.tuso, a.mauso);    c.mauso = a.mauso / UCLN(a.tuso, a.mauso);    return c; }  PS cong(PS a, PS b) {    PS c;    c.tuso = a.tuso * b.mauso + a.mauso * b.tuso;    c.mauso = a.mauso * b.mauso;    c = rutGon(c);    return c; }  PS tru(PS a, PS b) {    PS c;    c.tuso = a.tuso * b.mauso - a.mauso * b.tuso;    c.mauso = a.mauso * b.mauso;    c = rutGon(c);    return c; }  PS nhan(PS a, PS b) {    PS c;    c.tuso = a.tuso * b.tuso;    c.mauso = a.mauso * b.mauso;    c = rutGon(c);    return c; }  PS chia(PS a, PS b) {    PS c;    c.tuso = a.tuso * b.mauso;    c.mauso = a.mauso * b.tuso;    c = rutGon(c);    return c; }  void print(PS a) {    printf("%d/%d", a.tuso, a.mauso); }  int main() {    PS a, b, c;     printf("Nhập phân số a: ");    scanf("%d%d", &a.tuso, &a.mauso);     printf("Nhập phân số b: ");    scanf("%d%d", &b.tuso, &b.mauso);     printf("Tối giản a ta được: ");    a = rutGon(a);    print(a);     printf("Tối giản b ta được: ");    b = rutGon(b);    print(b);     printf("Tổng của hai phân số = ");    c = cong(a, b);    print(c);     printf("Hiệu của hai phân số = ");    c = tru(a, b);    print(c);     printf("Tích của hai phân số = ");    c = nhan(a, b);    print(c);     printf("Thương của hai phân số = ");    c = chia(a, b);    print(c);     return 0; }

Kết quả:

Nhập phân số a: 1 2 Nhập phân số b: 1 4 Tối giản a ta được: 1/2 Tối giản b ta được: 1/4 Tổng của hai phân số = 6/8 Hiệu của hai phân số = 2/8 Tích của hai phân số = 1/8 Thương của hai phân số = 4/8

Quản lý sách

#include  #include   struct Books {    char title[50];    char author[50];    char subject[100];    int book_id; };  int main() {    struct Books Book1;    struct Books Book2;     strcpy(Book1.title, "C Programming");    strcpy(Book1.author, "Nuha Ali");    strcpy(Book1.subject, "C Programming Tutorial");    Book1.book_id = 6495407;     strcpy(Book2.title, "Telecom Billing");    strcpy(Book2.author, "Zara Ali");    strcpy(Book2.subject, "Telecom Billing Tutorial");    Book2.book_id = 6495700;     printf("Book 1 title : %s\n", Book1.title);    printf("Book 1 author : %s\n", Book1.author);    printf("Book 1 subject : %s\n", Book1.subject);    printf("Book 1 book_id : %d\n", Book1.book_id);     printf("Book 2 title : %s\n", Book2.title);    printf("Book 2 author : %s\n", Book2.author);    printf("Book 2 subject : %s\n", Book2.subject);    printf("Book 2 book_id : %d\n", Book2.book_id);     return 0; }

Kết quả:

Book 1 title : C Programming Book 1 author : Nuha Ali Book 1 subject : C Programming Tutorial Book 1 book_id : 6495407 Book 2 title : Telecom Billing Book 2 author : Zara Ali Book 2 subject : Telecom Billing Tutorial Book 2 book_id : 6495700

Quản lý sách bằng con trỏ

#include  #include   struct Books {    char title[50];    char author[50];    char subject[100];    int book_id; };  void printBook(struct Books *book);  int main() {    struct Books Book1;    struct Books Book2;     strcpy(Book1.title, "C Programming");    strcpy(Book1.author, "Nuha Ali");    strcpy(Book1.subject, "C Programming Tutorial");    Book1.book_id = 6495407;     strcpy(Book2.title, "Telecom Billing");    strcpy(Book2.author, "Zara Ali");    strcpy(Book2.subject, "Telecom Billing Tutorial");    Book2.book_id = 6495700;     printBook(&Book1);    printBook(&Book2);     return 0; }  void printBook(struct Books *book) {    printf("Book title : %s\n", book->title);    printf("Book author : %s\n", book->author);    printf("Book subject : %s\n", book->subject);    printf("Book book_id : %d\n", book->book_id); }

Kết quả:

Book title : C Programming Book author : Nuha Ali Book subject : C Programming Tutorial Book book_id : 6495407 Book title : Telecom Billing Book author : Zara Ali Book subject : Telecom Billing Tutorial Book book_id : 6495700

Kết

Cấu trúc dữ liệu Struct được sử dụng rất nhiều trong lập trình C. Với Struct, chúng ta có thể quản lý thông tin một cách đơn giản hơn rất nhiều.

Nếu thấy có ích, hãy chia sẻ bài viết và tham gia nhóm Nghiện Lập Trình để giao lưu và học hỏi nhé!

1