寫在前面:
本文章旨在總結備份、方便以後查詢,由於是個人總結,如有不對,歡迎指正;另外,內容大部分來自網絡、書籍、和各類手冊,如若侵權請告知,馬上刪帖致歉。
一、涉及到的知識點
二、應用
結構體是一種數據的歸類方式,相比數組或變量更具有整體全面性,例如一個數組只可以放一些按照元素順序存放的單元變量,即 buffer = {x, x, x, x, x…},i 有多大,數組內元素就有多少。那麼我們這時候如果我們用這個數組來接收串口接收信息,信息的格式是: 數據頭 ->數據長度 ->數據區 ->數據校驗 ->數據尾
假設數據區爲 <姓名-身高-體重-性別-年齡>
那麼我們用數組接收時,提取數據的時候就需要計算出數據格式中每個單元所對應的位置,即數組中第 i 個元素對應的內容;這樣顯然是很麻煩的,效率低下,這就相當於先織了一個大網,捕捉到一網魚,還得過下稱,才能按照重量分類開來一樣
若是使用結構體來作數據傳輸,那麼只需要彼此約定俗成,注意一下事項就可以了
按照上面的,我們先來構建一下這個結構體
typedef struct
{
uint8_t head; // 數據頭
uint16_t datalen; // 數據長度
uint8_t name[10]; // 姓名
uint8_t height; // 身高
uint8_t weight; // 體重
uint32_t sex; // 性別
uint8_t age; // 年齡
uint8_t checksum; // 校驗和
uint8_t endmark; // 結束
} Profile_TypeDef;
1、發送
void Send_function(void)
{
Profile_TypeDef test;
test.head = 0x5A;
...
...
test.endmark = 0xFF;
USART_SendString((uint8_t *)&test, sizeof(test));
}
因爲串口數據發送是以 Byte爲單位,我們需要把結構體強制轉換一下
2、接收
void Recv_function( uint8_t *Buf )
{
Profile_TypeDef *receive = NULL;
receive = (Profile_TypeDef *)Buf;
receive->head = XXXX;
receive->datalen = YYYY;
...
...
}
你以爲這樣就結束了嗎?錯的,你還要注意一些東西
三、注意事項
1、大小端問題
假設你是用 STM32來通訊,那麼它的數據存儲方式是小端模式(這個看手冊,各款有着不同的數據存儲方式,具體以手冊爲準),即數據的高字節存儲在內存地址的高字節,低字節存儲在內存地址的低字節,如果串口接收到的數據是低字節在前,則拷貝來的數據就不用進行處理,如果接收的數據是高字節在前則需要對拷貝來的數據進行高低字節反轉,這個知識看開篇的 “字節序部分”
2、數據對齊問題
結構體存放的數據是緊密的連在一起放在內存的,但是,各個硬件平臺對存儲空間的處理上有很大的不同;一些平臺對某些特定類型的數據只能從某些特定地址開始存取,比如有些架構的 CPU在訪問一個沒有進行對齊的變量的時候會發生錯誤,那麼在這種架構下編程必須保證字節對齊;其他平臺可能沒有這種情況,但是最常見的是如果不按照適合其平臺要求對數據存放進行對齊,會在存取效率上帶來損失
結構體的對齊以及數據大小計算可以看上面的 “C語言結構體大小及對齊問題”
一般來說,我們構建一個結構體,它的對齊方式是按照編譯器的默認設置進行對齊的;然而,那麼我們是不是可以改變編譯器的這種默認對齊設置呢?這個是可以的,例如:
#pragma pack (2) /*指定按2字節對齊*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定對齊,恢復缺省對齊*/
#pragma pack (value)用於指定對齊值爲 value值,當然,這是在 gcc編譯器上的指定對齊操作,不同的編譯器有着自己的操作指令
-
keil mdk中,可以要查看一下 __align()的用法;
-
iar ewarm中,則查看一下 #pragma data_alignment的用法
四、最後
參考:
http://www.stmcu.org.cn/module/forum/thread-615994-1-1.html
https://www.topbyte.cn/2010/09/c-byte-alignment/
https://www.amobbs.com/thread-5600177-1-1.html