Kiểu cấu trúc (struct) trong C – Liên minh trong C

By 05/07/2021Tháng Bảy 7th, 2021C/C++, KIẾN THỨC LẬP TRÌNH

1. Struct trong C

Trong phần này, bạn sẽ được tìm hiểu các kiểu struct trong C. Bên cạnh đó, bạn cũng được học cách định nghĩa và sử dụng các cấu trúc (ngôn ngữ lập trình C) thông qua các ví dụ.

Struct là gì? Kiểu struct trong C (hay cấu trúc trong C) chính là một tập hợp các biến (có thể có nhiều kiểu khác nhau) trong cùng một tên duy nhất.

kiểu struct trong c1.1 Làm thế nào để xác định cấu trúc trong C?

Trước khi tạo ra các biến cấu trúc trong C, bạn cần phải xác định kiểu dữ liệu của nó. Để định nghĩa cấu trúc trong C, bạn cầu sử dụng từ khóa struct.

Cú pháp của cấu trúc trong C (ngôn ngữ lập trình)

struct structureName

{

dataType member1;

dataType member2;

};

Dưới đây là ví dụ:

struct Person

{

char name[50];

int citNo;

float salary;

};

Kiểu dẫn xuất struct Person được định nghĩa. Lúc này, bạn có thể tạo ra các biến thuộc kiểu này.

1.2 Tạo các biến cấu trúc trong C

Khi một kiểu struct C được khai báo, chúng không được cấp phát bộ nhớ hoặc bộ lưu trữ. Để cấp phát bộ nhớ cho kiểu cấu trúc này và làm việc với nó, bạn cần tạo thêm các biến.

Dưới đây là cách tôi tạo ra các biến struct trong C:

struct Person

{

char name[50];

int citNo;

float salary;

};

int main()

{

struct Person person1, person2, p[20];

return 0;

}

Có thể tạo biến cấu trúc trong C theo cách khác như sau:

struct Person

{

char name[50];

int citNo;

float salary;

} person1, person2, p[20];

Trong cả 2 trường hợp trên, 2 biến person1, person2, và biến mảng p có 20 phần tử thuộc kiểu struct Person được tạo ra.

1.3 Truy cập vào các thành viên của một cấu trúc

Sử dụng 2 kiểu toán tử dưới đây để truy cập vào các thành viên của struct C.

  1. . – Toán tử thành viên dot
  2. -> –Toán tử thành viên arrow (sẽ được giới thiệu trong những phần tiếp theo)

Giả sử bạn muốn truy cập vào salary của person2, bạn cần thực hiện như sau:

person2.salary

Ví dụ: Thêm hai khoảng cách

// Program to add two distances (feet-inch)

#include <stdio.h>

struct Distance

{

int feet;

float inch;

} dist1, dist2, sum;

int main()

{

printf(“1st distance\n”);

printf(“Enter feet: “);

scanf(“%d”, &dist1.feet);

printf(“Enter inch: “);

scanf(“%f”, &dist1.inch);

printf(“2nd distance\n”);

printf(“Enter feet: “);

scanf(“%d”, &dist2.feet);

printf(“Enter inch: “);

scanf(“%f”, &dist2.inch);

// adding feet

sum.feet = dist1.feet + dist2.feet;

// adding inches

sum.inch = dist1.inch + dist2.inch;

// changing to feet if inch is greater than 12

while (sum.inch >= 12)

{

++sum.feet;

sum.inch = sum.inch – 12;

}

printf(“Sum of distances = %d\’-%.1f\””, sum.feet, sum.inch);

return 0;

}

Đầu ra

1st distance

Enter feet: 12

Enter inch: 7.9

2nd distance

Enter feet: 2

Enter inch: 9.8

Sum of distances = 15′-5.7″

1.4 Từ khóa typedef trong C

Tôi sử dụng từ khóa typedef trong C để tạo tên bí danh cho các kiểu dữ liệu. Nó thường được sử dụng với các kiểu cấu trúc trong C để làm đơn giản hóa cú pháp khai báo biến.

Mã dưới đây

struct Distance{

int feet;

float inch;

};

int main() {

struct Distance d1, d2;

}

Tương đương với

typedef struct Distance{

int feet;

float inch;

} distances;

int main() {

distances d1, d2;

}

1.5 Cấu trúc Struct lồng nhau

Bạn có thể tạo một cấu trúc lồng trong một cấu trúc khác trong lập trình C. Ví dụ,

struct complex

{

int imag;

float real;

};

struct number

{

struct complex comp;

int integers;

} num1, num2;

Giả sử bạn muốn cài đặt imag của biến num2 là 11. Dưới đây là cách thức thực hiện:

num2.comp.imag = 11;

1.6 Tại sao lại cần cấu trúc trong C?

Giả sử nếu bạn muốn lưu trữ thông tin về một người: tên, số CMND, mức lương. Bạn có thể tạo ra các biến name, IdNo và salary khác nhau để lưu trữ những thông tin này.

Vậy nếu bạn muốn lưu trữ thông tin của nhiều người thì sao? Trong trường hợp này, bạn cần tạo nhiều biến cho từng thông tin của từng người: name1, IdNo1, salary1, name2, IdNo2, salary2,…

Một cách tiếp cận tốt hơn chính là nên tạo ra một bộ sưu tập chứa các thông tin liên quan trong một cấu trúc đơn lé Person và sử dụng nó cho tất cả mọi người.

Tìm hiểu thêm về kiểu cấu trúc trong C:

  • Cấu trúc và biến pointer trong C
  • Truyền cấu trúc vào hàm

2. Cấu trúc và biến pointer trong C

Trong phần này, bạn sẽ được tìm hiểu cách sử dụng biến con trỏ để truy cập vào các thành viên của struct trong C. Bên cạnh đó, bạn cũng sẽ biết được cách cấp phát bộ nhớ một cách tự động của các kiểu cấu trúc trong C.

Nhưng trước đó, bạn cần xem qua những phần dưới đây để tìm hiểu biến pointer được sử dụng như thế nào với cấu trúc:

  • Biến pointer trong C
  • Cấu trúc trong C

2.1 Tạo biến pointer trỏ đến cấu trúc trong C

Dưới đây là cách mà bạn có thể tạo biến pointer trỏ đến struct trong C

struct name {

member1;

member2;

.

.

};

int main()

{

struct name *ptr, Harry;

}

Trong trường hợp này, ptr là một biến pointer trỏ đến struct.

Ví dụ: Truy cập vào các thành viên bằng biến pointer

Để truy cập vào các thành viên của một cấu trúc bằng cách sử dụng biến pointer, bạn cần sử dụng toán tử ->.

#include <stdio.h>

struct person

{

int age;

float weight;

};

int main()

{

struct person *personPtr, person1;

personPtr = &person1;

printf(“Enter age: “);

scanf(“%d”, &personPtr->age);

printf(“Enter weight: “);

scanf(“%f”, &personPtr->weight);

printf(“Displaying:\n”);

printf(“Age: %d\n”, personPtr->age);

printf(“weight: %f”, personPtr->weight);

return 0;

}

Trong ví dụ này, địa chỉ của person1 được lưu trữ trong biến pointer personPtr bằng cách sử dụng personPtr = &person1;.

Bây giờ, bạn có thể truy cập vào các thành viên của person1 bằng cách sử dụng biến pointer personPtr.

Bên cạnh đó,

  • personPtr->age tương đương với (*personPtr).age
  • personPtr->weight tương đương với (*personPtr).weight

2.2 Cấp phát bộ nhớ động của cấu trúc

Trước khi tìm hiểu phần này, tôi khuyên bạn nên xem lại phần cấp phát bộ nhớ động.

Đôi khi, số lượng biến cấu trúc mà bạn đã khai báo có thể không đủ. Bạn cần cấp phát bộ nhớ trong quá trình lập trình chạy. Dưới đây là cách để thực hiện điều đó trong lập trình C.

Ví dụ: Cấp phát bộ nhớ động của cấu trúc

#include <stdio.h>

#include <stdlib.h>

struct person {

int age;

float weight;

char name[30];

};

int main()

{

struct person *ptr;

int i, n;

printf(“Enter the number of persons: “);

scanf(“%d”, &n);

// allocating memory for n numbers of struct person

ptr = (struct person*) malloc(n * sizeof(struct person));

for(i = 0; i < n; ++i)

{

printf(“Enter first name and age respectively: “);

// To access members of 1st struct person,

// ptr->name and ptr->age is used

// To access members of 2nd struct person,

// (ptr+1)->name and (ptr+1)->age is used

scanf(“%s %d”, (ptr+i)->name, &(ptr+i)->age);

}

printf(“Displaying Information:\n”);

for(i = 0; i < n; ++i)

printf(“Name: %s\tAge: %d\n”, (ptr+i)->name, (ptr+i)->age);

return 0;

}

Khi bạn chạy lập trình, kết quả đầu ra sẽ là:

Enter the number of persons:  2

Enter first name and age respectively:  Harry 24

Enter first name and age respectively:  Gary 32

Displaying Information:

Name: Harry       Age: 24

Name: Gary         Age: 32

Trong ví dụ này, n biến cấu trúc được tạo ra. Lưu ý, n là do người dùng nhập vào.

Để cấp phát bộ nhớ cho n biến struct person, tôi đã sử dụng,

ptr = (struct person*) malloc(n * sizeof(struct person));

Sau đó, tôi sử dụng biến pointer ptr để truy cập vào các phần tử của person.

3. Cấu trúc và hàm trong lập trình C

Trong phần này, bạn sẽ được tìm hiểu về cách truyền biến cấu trúc dưới dạng đối số vào hàm. Bên cạnh đó, bạn cũng sẽ biết được cách lấy lại cấu trúc từ hàm thông qua các ví dụ.

Tương tự như các biến thuộc kiểu xây dựng sẵn, bạn cũng có thể truyền các biến struct trong C vào hàm.

3.1 Truyền cấu trúc vào hàm

Trước tiên, bạn cần xem lại các phần dưới đây trước khi tìm hiểu cách truyền cấu trúc vào hàm

  • Cấu trúc trong C
  • Hàm trong lập trình CC
  • Hàm do người dùng định nghĩa

Dưới đây là cách mà bạn có thể truyền các cấu trúc vào một hàm

#include <stdio.h>

struct student {

char name[50];

int age;

};

// function prototype

void display(struct student s);

int main() {

struct student s1;

printf(“Enter name: “);

// read string input from the user until \n is entered

// \n is discarded

scanf(“%[^\n]%*c”, s1.name);

printf(“Enter age: “);

scanf(“%d”, &s1.age);

display(s1); // passing struct as an argument

return 0;

}

void display(struct student s) {

printf(“\nDisplaying information\n”);

printf(“Name: %s”, s.name);

printf(“\nAge: %d”, s.age);

}

Đầu ra

Enter name: Bond

Enter age: 13

Displaying information

Name: Bond

Age: 13

Biến cấu trúc s1 của kiểu struct student được tạo ra. Biến này sẽ được truyền vào hàm display() bằng cách sử dụng câu lệnh display(s1);

3.2 Lấy lại cấu trúc từ một hàm

Dưới đây là cách bạn có thể lấy lại cấu trúc từ một hàm:

#include <stdio.h>

struct student

{

char name[50];

int age;

};

// function prototype

struct student getInformation();

int main()

{

struct student s;

s = getInformation();

printf(“\nDisplaying information\n”);

printf(“Name: %s”, s.name);

printf(“\nRoll: %d”, s.age);

return 0;

}

struct student getInformation()

{

struct student s1;

printf(“Enter name: “);

scanf (“%[^\n]%*c”, s1.name);

printf(“Enter age: “);

scanf(“%d”, &s1.age);

return s1;

}

Hàm getInformation() được gọi bằng cách sử dụng câu lệnh s = getInformation();. Hàm này trả về một cấu trúc của kiểu struct student. Cấu trúc trả về sẽ được hiển thị từ hàm main().

Lưu ý, kiểu trả về của hàm getInformation() cũng là struct student.

3.3 Truyền cấu trúc bằng cách tham chiếu

Bạn cũng có thể truyền cấu trúc bằng cách tham chiếu (tương tự như cách mà bạn truyền biến thuộc kiểu xây dựng sẵn bằng cách tham chiếu). Tôi khuyên bạn nên xem lại phần truyền bằng cách tham chiếu trước khi xem những thông tin tiếp theo.

Trong quá trình truyền bằng cách tham chiếu, địa chỉ bộ nhớ của các biến struct trong C được truyền vào hàm.

#include <stdio.h>

typedef struct Complex

{

float real;

float imag;

} complex;

void addNumbers(complex c1, complex c2, complex *result);

int main()

{

complex c1, c2, result;

printf(“For first number,\n”);

printf(“Enter real part: “);

scanf(“%f”, &c1.real);

printf(“Enter imaginary part: “);

scanf(“%f”, &c1.imag);

printf(“For second number, \n”);

printf(“Enter real part: “);

scanf(“%f”, &c2.real);

printf(“Enter imaginary part: “);

scanf(“%f”, &c2.imag);

addNumbers(c1, c2, &result);

printf(“\nresult.real = %.1f\n”, result.real);

printf(“result.imag = %.1f”, result.imag);

return 0;

}

void addNumbers(complex c1, complex c2, complex *result)

{

result->real = c1.real + c2.real;

result->imag = c1.imag + c2.imag;

}

Đầu ra

For first number,

Enter real part:  1.1

Enter imaginary part:  -2.4

For second number,

Enter real part:  3.4

Enter imaginary part:  -3.2

result.real = 4.5

result.imag = -5.6

Trong lập trình trên, ba biến cấu trúc c1, c2 và địa chỉ của result được truyền vào hàm addNumbers(). Tại đây, result được truyền bằng cách tham chiếu.

Khi biến result trong hàm addNumbers() bị thay đổi thì biến result trong hàm main() cũng thay đổi theo.

4. Liên minh trong C

Trong phần này, bạn sẽ được tìm hiểu về các liên minh trong lập trình C. Cụ thể là: Làm thế nào để tạo ra các liên minh, truy cập vào các thành viên của nó và sự khác biệt giữa liên minh và cấu trúc.

Liên minh (union) là một kiểu do người dùng định nghĩa, tương tự như cấu trúc trong C, nhưng cũng có một vài điểm khác biệt. Cấu trúc cấp phát đủ không gian để lưu trữ tất cả các thành viên của nó, trong khi, liên minh chỉ cấp phát không gian để lưu trữ những thành viên lớn nhất.

4.1 Làm thế nào để định nghĩa liên minh?

Tôi sử dụng từ khóa union để định nghĩa liên minh. Ví dụ,

union car

{

char name[50];

int price;

};

Đoạn mã trên đây định nghĩa kiểu dẫn xuất union car.

4.2 Tạo các biến liên minh

Khi một liên minh được định nghĩa, nó sẽ tạo ra một kiểu do người dùng định nghĩa. Thế nhưng, nó lại không được cấp phát bộ nhớ. Để cấp phát bộ nhớ cho một kiểu liên minh nhất định và làm việc với nó, tôi sẽ tạo các biến:

Dưới đây là cách để tạo các biến liên minh.

union car

{

char name[50];

int price;

};

int main()

{

union car car1, car2, *car3;

return 0;

}

Một cách khác để tạo các biến liên minh là:

union car

{

char name[50];

int price;

} car1, car2, *car3

Trong 2 trường hợp trên, biến liên minh car1, car2 và biến pointer car3 của kiểu union car được tạo ra.

4.3 Truy cập vào các thành viên của một liên minh

Tôi sử dụng toán tử . để truy cập vào các thành viên của một liên minh. Để truy cập các biến pointer, tôi sẽ sử dụng toán tử->.

Trong ví dụ trên,

  • Để truy cập price của car1, tôi sử dụng car1.price
  • Để truy cập price của car3, tôi sử dụng (*car3).price hoặc car3->price

4.4 Sự khác biệt giữa liên minh và cấu trúc

Hãy xem ví dụ dưới đây để hiểu rõ sự khác biệt giữa liên minh và cấu trúc:

#include <stdio.h>

union unionJob

{

//defining a union

char name[32];

float salary;

int workerNo;

} uJob;

struct structJob

{

char name[32];

float salary;

int workerNo;

} sJob;

int main()

{

printf(“size of union = %d bytes”, sizeof(uJob));

printf(“\nsize of structure = %d bytes”, sizeof(sJob));

return 0;

}

Đầu ra

size of union = 32

size of structure = 40

4.5 Tại sao lại có sự khác biệt về kích thước của biến liên minh và biến cấu trúc?

Kích thước của sJob là 40 byte vì

  • kích thước của name[32] là 32 byte
  • kích thước của salary là 4 byte
  • kích thước của workerNo là 4 byte

Tuy nhiên, kích thước của uJob là 32  byte. Bởi vì kích thước của biến liên minh thường sẽ là kích thước của phần tử lớn nhất của nó. Trong ví dụ trên, kích thước của phần tử lớn nhất, (name[32]), là 32 byte.

Tất cả các thành viên trong cùng một liên minh cùng chung một bộ nhớ giống nhau.

Ví dụ: Truy cập vào các thành viên của liên minh

#include <stdio.h>

union Job {

float salary;

int workerNo;

} j;

int main() {

j.salary = 12.3;

// when j.workerNo is assigned a value,

// j.salary will no longer hold 12.3

j.workerNo = 100;

printf(“Salary = %.1f\n”, j.salary);

printf(“Number of workers = %d”, j.workerNo);

return 0;

}

Đầu ra

Salary = 0.0

Number of workers = 100

Để lại một câu trả lời