🌱 Embedded C - Embedded C - User-Defined Data Type - Kiểu dữ liệu Struct
Ở bài viết về Embedded C Data Type, chúng ta đã nhắc đến User-Defined Data Type (Kiểu dữ liệu người dùng tự định nghĩa). C hỗ trợ 3 kiểu User-Defined Data Type: Struct, Union và Enum.
Bài viết này sẽ đi chi tiết vào Struct, kiểu dữ liệu được sử dụng thường xuyên và rất quan trọng trong lập trình Embedded C.
Mục Lục
- Tạo một Struct
- Sử dụng một Struct
- Struct Alignment Padding
- Struct & Alignment Video Demo
Tại sao cần có Struct?
Trước đó chúng ta đã bàn về Array, với một ví dụ: "Khi cần lưu trữ nhiệt độ, ta tạo một biến float temp;. Nhưng khi cần lưu trữ vào quản lý nhiều dữ liệu hơn, chẳng hạn lưu trữ lịch sử nhiệt độ 100 lần gần nhất, việc tạo ra 100 biến sẽ thực sự khó khăn trong việc quản lý (float temp1, float temp2, ...; 😰), nên chúng ta có thể tạo Array ↪ float temp[100];"
Ở một trường hợp khác, nếu ta cần lưu trữ các thông tin khác kiểu dữ liệu, ví dụ thời gian, độ ẩm, ... thì việc tạo nhiều mảng cũng sẽ gây ra khó quản lý.
Ví dụ: int time[100]; float temp[100]; int humidity[100]; ➜ khá ổn nếu chỉ lưu một node cảm biến, nếu có nhiều node hơn, thì lại phải tạo nhiều mảng như vậy với cái tên time1
😰
Chính vì vậy, C hỗ trợ một kiểu dữ liệu có thể lưu trữ các thành phần với data type khác nhau, còn lưu gì thì để người dùng tự định nghĩa vì mỗi bài toán sẽ lưu thông tin khác nhau, đó chính là Struct.
Definition: In C programming, a struct (or structure) is a collection of variables (can be of different types) under a single name.
Tạo một Struct
Trước khi sử dụng, dev cần tạo một struct (Data Type) sử dụng struct keyword.
struct structureName {
dataType member1;
dataType member2;
...
};
Các thành phần bên trong struct (item / element) có thể là một biến, một mảng, một struct, ... (hoặc bất cứ kiểu dữ liệu nào bạn đã biết).
Một vài ví dụ về struct:
struct Sensor {
int time;
float temp;
int humidity;
};
struct Person {
char name[50]; // array in struct
int citNo;
float salary;
};
struct infor {
struct Person per; // nested struct
int hasgtag;
};
Các struct trên là chúng ta đã tạo ra các kiểu dữ liệu (User-defined Data Type), chứ chưa phải các biến, tức là bản thân chúng không chiếm bộ nhớ. Dưới đây là ví dụ về việc tạo các biến với kiểu dữ liệu struct đã tạo:
struct Sensor Data1; // Un-Initialized Variable
struct Sensor Data2 = { 1, 30.5, 40 }; // Initialized Variable
struct Sensor Data3 = { .time = 1, .temp = 30.5, .humidity = 40 }; // Initialized Variable
struct Sensor Data4 = { .time = 1, .temp = 30.5 }; // .humidity = 0 default
typedef keyword
Dễ thấy với cách khai báo trên, từ "struct" sẽ thường khiến việc viết code trở nên dài dòng, có thể sử dụng từ khóa typedef để rút gọn tên.
Ví dụ:
typedef struct {
int time;
float temp;
int humidity;
} Sensor_t; // Create Data Type
// Create Variable
Sensor_t Data = { .time = 1, .temp = 30.5, .humidity = 40 };
Các Data Type được tạo ra nên thêm đuôi '_t' hoặc 'Type' để đánh dấu đây là một kiểu dữ liệu (học tập cách làm của thư viện stdint.h).
Sử dụng một Struct
Giống như một biến thông thường, một biến tạo ra theo kiểu struct cũng có những đặc điểm tương tự:
- Identifier, Chiếm bộ nhớ, có địa chỉ
- Có thể tạo mảng các struct
- Có thể truyền vào hàm và trả về hàm
Một số điểm khác biệt:
struct chứa nhiều element nên cần truy xuất từng phần tử
Để truy cập đến phần tử bên trong struct, sử dụng toán tử '.' (Đối với struct pointer có thể sử dụng toán tử ➔ sẽ được nhắc đến ở phần sau).
- #include <stdio.h>
- #include <string.h>
- typedef struct {
- int time;
- float temp;
- int humidity;
- } Sensor_t; // Create Data Type
- int main() {
- // Create Variable
- Sensor_t Data;
- // assign values struct element
- Data.time = 1;
- Data.temp = 30.5;
- Data.humidity = 40;
- // print struct variables
- printf("Time: %d\n", Data.time);
- printf("Temperature: %f\n", Data.temp);
- printf("Huminity : %d", Data.humidity);
- return 0;
- }
Kích thước của struct
Theo logic thông thường thì kích thước struct có lẽ bằng tổng kích thước các phần tử bên trong nó. Tuy nhiên thực tế thì kết quả lại khác! Hãy thử đoạn code sau:
- #include <stdio.h>
- #include <stdint.h>
- typedef struct {
- uint8_t A;
- uint32_t B;
- } TestType;
- int main() {
- printf("Size of TestType: %d", sizeof(TestType)); // 8
- return 0;
- }
Dễ thấy kết quả là 8 thay vì 5. Điều này là kết quả của khái niệm Alignment - Padding Struct sẽ được đề cập ở dưới.
Truyền struct vào hàm
Với khái niệm Alignment kể trên, kích thước struct luôn ≥ tổng kích thước các phần tử trong nó, vậy khi truyền struct vào hàm hoặc hàm trả về struct, việc stacking khi function call sẽ chiếm một số lượng bộ nhớ khá lớn.
↪ Chính vì vậy, việc truyền struct lớn vào hàm thường được sử dụng dưới dạng Pass by Reference thay vì Pass by Value để giảm thiểu tiêu tốn bộ nhớ stack.
Struct Alignment Padding
Phần này khá nhiều nội dung nên mình đã tổng hợp trong bài viết này!
Struct & Alignment Video Demo
>>>>>> Follow ngay <<<<<<<
Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊