- Các kiểu dùng struct
-
Theo luật C/C++, tổng kích thước của một
structphải là bội số của kiểu dữ liệu có kích thước lớn nhất bên trong nó. -
Kiểu dữ liệu lớn nhất trong
structcủa bạn làuint16_t(2 byte). Do đó, tổng kích thướcstructphải chia hết cho 2. -
Cùng tính tổng kích thước các thành phần:
1 (header) + 1 (padding) + 2 (ID) + 255 (pay) = 259 byte. -
Vì
259không chia hết cho 2, trình biên dịch phải chèn thêm 1 byte rỗng ở cuối cùng để làm tròn tổng kích thước lên 260 byte.
struct trực tiếp vào địa chỉ vật lý của thanh ghi, bắt buộc phải vô hiệu hóa tính năng tự động đệm byte (implicit padding) của trình biên dịch. Nếu không làm vậy, trình biên dịch có thể tự động chèn thêm byte vào giữa các biến để tối ưu tốc độ đọc/ghi, khiến cấu trúc struct bị lệch địa chỉ so với thanh ghi thực tế.Mặc dù pragma pack(1) giúp địa chỉ liên tiếp và tiết kiệm bộ nhớ, bạn cần lưu ý hai vấn đề sau khi lập trình nhúng hoặc hệ thống:
-
Giảm tốc độ CPU: CPU (đặc biệt là dòng 32-bit hoặc 64-bit) tối ưu nhất khi đọc dữ liệu từ các địa chỉ chia hết cho 2 hoặc 4. Khi bạn ép
ID(2 byte) nằm ở địa chỉ lẻ (ví dụ vị trí số 1), CPU có thể phải tốn 2 chu kỳ lệnh để đọc xong biến này (thay vì 1 chu kỳ nếu nó nằm ở địa chỉ chẵn). -
Nguy cơ lỗi HardFault (Crash chip): Một số dòng vi điều khiển cũ hoặc kiến trúc ARM Cortex-M0/M0+ không hỗ trợ đọc dữ liệu không căn lề (unaligned access). Nếu bạn cố tình đọc biến
uint16_thoặcuint32_tnằm ở địa chỉ lẻ, chương trình có thể bị treo ngay lập tức (gây ra lỗi phần cứng HardFault).
Lời khuyên: Chỉ nên dùng pragma pack(1) khi bạn cần map cấu trúc dữ liệu chuẩn xác với thanh ghi phần cứng hoặc với định dạng gói tin truyền qua mạng (TCP/UDP, Serial). Đối với các struct khai báo biến thông thường trong phần mềm, hãy để trình biên dịch tự động padding để đạt hiệu năng xử lý cao nhất.