2013-10-09 78 views
1

在結構上使用memcpy時遇到問題。結構使用memcpy

考慮以下結構

struct HEADER 
{ 
    unsigned int preamble; 
    unsigned char length; 
    unsigned char control; 
    unsigned int destination; 
    unsigned int source; 
    unsigned int crc; 
} 

如果我使用memcpy將數據從接收緩衝區拷貝到這個結構的副本是好的,但如果我重新聲明結構爲以下內容:

struct HEADER 
{ 
    unsigned int preamble; 
    unsigned char length; 
    struct CONTROL control; 
    unsigned int destination; 
    unsigned int source; 
    unsigned int crc; 
} 

struct CONTROL 
{ 
    unsigned dir : 1; 
    unsigned prm : 1; 
    unsigned fcb : 1; 
    unsigned fcb : 1; 
    unsigned function_code : 4; 
} 

現在如果我使用與以前相同的memcpy代碼,則前兩個變量(前導碼和長度)被複制OK。控制是完全搞砸了,最後三個變量轉移了一個,也就是crc = 0,source = crc,destination = source ...

ANyone對我有什麼好的建議嗎?

+2

您還應該顯示您使用的'memcpy'代碼。 – user694733

+0

你確定,在HEADER改變後,接收緩衝區有完全相同的改變? – zoska

回答

2

當您在中間添加control時,是否知道接收緩衝區中的格式正確?

無論如何,你的問題是位域是這裏的錯誤工具:你不能依賴內存中的佈局,特別是任何特定的內容,至少與你爲序列化表單選擇的內容完全相同。

嘗試將結構直接複製到/從外部存儲器幾乎不是一個好主意;你需要正確的序列化。編譯器可以在結構的字段之間添加填充和對齊,並且使用位域使其更加糟糕。不要這樣做。

實施適當的序列化/反序列化功能:

unsigned char * header_serialize(unsigned char *put, const struct HEADER *h); 
unsigned char * header_deserialize(unsigned char *get, struct HEADER *h); 

那經過結構和讀/你覺得需要(可能爲每個字段)寫入的字節數:

static unsigned char * uint32_serialize(unsigned char *put, uint32_t x) 
{ 
    *put++ = (x >> 24) & 255; 
    *put++ = (x >> 16) & 255; 
    *put++ = (x >> 8) & 255; 
    *put++ = x & 255; 
    return put; 
} 

unsigned char * header_serialize(unsigned char *put, const struct HEADER *h) 
{ 
    const uint8_t ctrl_serialized = (h->control.dir << 7) | 
            (h->control.prm << 6) | 
            (h->control.fcb << 5) | 
            (h->control.function_code); 

    put = uint32_serialize(put, h->preamble); 
    *put++ = h->length; 
    *put++ = ctrl_serialized; 
    put = uint32_serialize(put, h->destination); 
    put = uint32_serialize(put, h->source); 
    put = uint32_serialize(put, h->crc); 

    return put; 
} 

注這需要明確序列化數據的字節序,這是你應該關心的事情(我使用了big-endian)。它還明確構建control字段的一個uint8_t版本,假定使用了struct版本。

另請注意,您的CONTROL聲明中存在拼寫錯誤; fcb發生兩次。

+0

您能否介紹一下序列化函數的作用?從來沒有使用過類似的東西...... – Aune

+0

當查看指針存儲器(指向結構體的指針)時,數據正確地位於那裏,但是當查看手錶時,數據被嚇倒... – Aune

+0

@ user1244472在序列化函數你應該將每個結構成員轉換爲字節。 – user694733

0

使用struct CONTROL control;而不是unsigned char control;會導致結構內部有不同的對齊方式,因此用memcpy()填充它會產生不同的結果。

0

Memcpy將源指向的位置的字節值直接複製到目標指向的內存塊。

源函數和目標函數指針所指向的對象的基礎類型與此函數無關;結果是數據的二進制副本。 所以,如果有任何結構填充,那麼你會弄亂結果。

0

檢查sizeof(struct CONTROL) - 我認爲這將是2或4取決於機器。由於您使用的是unsigned位字段(並且unsignedunsigned int的縮寫),因此整個結構(struct CONTROL)至少需要無符號整數的大小 - 即2或4個字節。使用unsigned char control需要1個字節的字段。所以,肯定應該與control變量不匹配。

嘗試改寫如下struct control: -

struct CONTROL 
{ 
    unsigned char dir : 1; 
    unsigned char prm : 1; 
    unsigned char fcb : 1; 
    unsigned char fcb : 1; 
    unsigned char function_code : 4; 
} 
+0

使用無符號字符x:1;是非法的 – Aune

+0

@ user1244472:不是。閱讀有關位域的信息。 – wildplasser

+0

當我嘗試這個時,我得到編譯器錯誤「非法位域類型」 – Aune

0

清潔的方法是使用一個工會,像:

struct HEADER 
{ 
    unsigned int preamble; 
    unsigned char length; 
    union { 
     unsigned char all; 
     struct CONTROL control; 
     } uni; 
    unsigned int destination; 
    unsigned int source; 
    unsigned int crc; 
}; 

struct的用戶則可以選擇的方式他想要訪問這個東西。

struct HEADER thing = {... }; 

if (thing.uni.control.dir) { ...} 

#if (!FULL_MOON) /* Update: stacking of bits within a word appears to depend on the phase of the moon */ 
if (thing.uni.all & 1) { ... } 
#else 
if (thing.uni.all & 0x80) { ... } 
#endif 

注:此構造做解決排列順序問題,這將需要隱式轉換。注意2:你也必須檢查編譯器的bit-endianness。


還要注意的是位域不是非常有用,特別是如果數據發送到線路上,並且代碼需要在不同的平臺上運行,用不同的排列和/或字節順序。 Plain unsigned charuint8_t加上一些位掩碼產生更乾淨的代碼。例如,檢查BSD或Linux內核中的IP堆棧。

+0

單獨聯合將無法工作,因爲'all'和'control'-成員具有不同的大小。 – user694733

+0

你能否詳細解釋一下如何使用它? – Aune

+0

結構控制是8位 – Aune