每天資訊記憶體不夠用?指標拆組包試過沒!

菜單

記憶體不夠用?指標拆組包試過沒!

今天的是乾貨,是真的幹。作為一個從業4年,工齡6年的嵌入式開發程式設計師,在做通訊裝置領域時最痛苦的事莫過於資料流過大問題,以及資料轉換時各種組包拆包等問題,特別是在控制器記憶體資源有限時。這種問題更加突出。

記憶體不夠用?指標拆組包試過沒!

拿乙太網+串列埠傳輸modbus指令舉例:

乙太網資料收發時乙太網的協議棧佔用記憶體約20Kbyte,單個SOCKET傳輸資料時最少預留了532byte位元組的快取空間,當取出網路下傳的資料時,需要拆掉網路傳輸頭並在末尾增加CRC,串列埠上行資料時,需要增加網路傳輸資料頭以及去掉末尾CRC值,如果此時碰到慣用for迴圈copy資料或者使用memcpy函式進行資料組包的大佬,記憶體空間基本就是捉襟見肘。

解決以上問題的辦法之一就是使用指標配合陣列進行組包與拆包,如下圖;

記憶體不夠用?指標拆組包試過沒!

網路端向串列埠傳送:宣告陣列時申明比實際資料所需空間更大的陣列unsigned char Buff[592],以及一個指標unsigned char *datapoint;將datapoint指向Buff[46]的位置,讀入資料,將資料從指標datapoint指向的位置開始存入陣列,同時使用另外的變數unsigned int length儲存當次讀入的資料的長度,進行資料拆包操作時(去包頭6位元組),直接將指標datapoint指向Buff[52]的位置,隨後將計算所得CRC(2byte)值從Buff[46 + length]位置存入陣列,並得出最終需要傳輸的長度length+2,串列埠傳送資料只需要輸入起始地址datapoint,長度length,將資料存入串列埠傳送環形佇列即可完成傳送。

程式碼如下

unsigned char Buff[592];

unsigned char *datapoint;

unsigned int length;

datapoint = & Buff[46];//指標指向陣列中間

net_dataread(datapoint,& length);//讀取資料,獲得資料長度

CRC(&Buff[46+length],datapoint,& length);//獲取校驗值並存入陣列

Uart_send(datapoint,length+2);

串列埠向網路傳送時,資料從環形佇列讀出,需要加上TCPmodbus協議的包頭部分(6byte),去掉CRC校驗,同樣宣告陣列以及指標、長度變數,將datapoint指向Buff[46]的位置,讀入資料,將資料從指標datapoint指向的位置開始存入陣列;將指標指向當前指向地址的前6個地址位置存入6位元組,然後按照新的datapoint位置,長度length+4將資料從網路發出;

程式碼如下

unsigned char Buff[592];

unsigned char *datapoint;

unsigned int length;

datapoint = & Buff[46];//指標指向陣列中間

uart_read(datapoint,& length);//讀取資料,獲得資料長度

datapoint = datapoint-6;

memcpy(datapoint,包頭暫存陣列首地址,6);

Uart_send(datapoint,length+4);

以上例項簡單說明了利用指標組包的簡單應用,但是在實際應用中可能由於裝置需要使用多種協議,組包方式不同,例如包頭之前加入註冊包等,資料實際讀取完成之後不是直接操作而是直接傳入子函式,進行組包與拆包,此時僅需要傳入datapoint與長度,進子函式,由於datapoint指向的陣列前後均有預留空間,子函式內依然可以使用指標,且在Buff[]陣列的範圍內,指標可以前後自由移動,在不需要進行大量資料複製的前提下完成陣列的拆組包過程,同時也避免了反覆複製資料對空間的浪費。

以上就是指標的簡單應用之一,指標除了對陣列,還可以對函式操作,結合結構體,基本上可以在C語言中實現類似C++中類功能。