簡體   English   中英

如何在沒有未對齊模式的情況下訪問打包結構中的變量?

[英]How can I access variables in packed structure without unaligned mode?

我正在使用打包結構進行直接DMA訪問的通信,這是我的測試代碼:

// structure for communication buf 1
typedef __packed struct _test1
{
    uint8_t a;
    uint32_t b;
    uint16_t c;
    uint16_t d;
    uint32_t e;
} test1;

// structure for communication buf 2
.
.
.
// structure for communication buf 3
.
.
.

// structure for communication buf set
typedef __packed struct _test2
{
    uint8_t dump[3];
    test1 t;
    // may have many other packed structure for communication buf
} test2;

#pragma anon_unions

typedef struct _test3
{
    union
    {
        uint32_t buf[4];
        __packed struct
        {
            __packed uint8_t dump[3];
            test1 t;
        };
    };
} test3;

test1 t1;
test2 t2;
test3 t3;

這些結構的大小是

sizeof(t1) = 13
sizeof(t2) = 16
sizeof(t3) = 16

如果我想訪問變量b,為了不影響性能,需要具有對齊訪問權限的讀/寫內存內容,手動計算偏移量

t3.buf[1]

但是我不能在不使用未對齊訪問的情況下在結構中讀/寫變量

t2.t.b
t3.t.b

所以我定義了如下代碼的結構,只包含變量a

typedef struct _test4
{
    __packed uint8_t a;
    uint32_t b;
    uint16_t c;
    uint16_t d;
    uint32_t e;
} test4;

typedef struct _test5
{
    __packed uint8_t dump[3];
    test4 t;
} test5;

test4 t4;
test5 t5;

盡管對齊了結構中所有元素的訪問,但也插入了填充

sizeof(t4) = 16
sizeof(t5) = 20

那么如何定義打包結構,並在不使用未對齊訪問的情況下訪問其中的單個變量(除了a)?

非常感謝你的幫助

你的問題引入了兩個問題:

  • 組件和/或設備之間的通信; 這可能有也可能沒有結構和整數的相同底層表示,因此您使用了非可移植的__packed屬性。
  • 訪問性能,對齊和/或數據大小的偏差; 一方面,編譯器將數據對齊以與總線一致,但另一方面,數據可能占用緩存中的太多空間。

其中一個是您要解決的實際問題,X,另一個是XY問題中的Y. 請避免將來問XY問題。

你有沒有考慮過如何根據你的要求保證uint16_tuint32_t是big endian還是little endian? 如果您關心可移植性,則需要指定。 我關心可移植性,所以這就是我的答案所關注的。 您可能還會注意到如何獲得最佳效率。 盡管如此,要使這個便攜:

  • 應該使用序列化函數對數據進行序列化,以通過除法 (或左移二進制和 )運算將結構的每個成員轉換為字節序列。
  • 同樣,您應該通過逆運算乘法加法 (或右移二進制或 )來反序列化您的數據。

舉個例子,這里有一些代碼顯示了用於序列化和反序列化test1 小端大端

typedef /*__packed*/ struct test1
{
    uint32_t b;
    uint32_t e;
    uint16_t c;
    uint16_t d;
    uint8_t a;
} test1;

void serialise_test1(test1 *destination, void *source) {
    uint8_t *s = source;
    destination->a = s[0];
    destination->b = s[1] * 0x01000000UL
                   + s[2] * 0x00010000UL
                   + s[3] * 0x00000100UL
                   + s[4];                 /* big endian */
    destination->c = s[5] * 0x0100U
                   + s[6];                 /* big endian */
    destination->d = s[7]
                   + s[8] * 0x0100U;       /* little endian */
    destination->e = s[9]
                   + s[10] * 0x00000100UL
                   + s[11] * 0x00010000UL
                   + s[12] * 0x01000000UL; /* little endian */
}

void deserialise_test1(void *destination, test1 *source) {
    uint8_t temp[] = { source->a
                     , source->b >> 24, source->b >> 16
                                      , source->b >> 8, source->b
                     , source->c >> 8, source->c
                     , source->d, source->d >> 8
                                , source->d >> 16, source->b >> 24 };
    memcpy(destination, temp, sizeof temp);
}

您可能會注意到我刪除了__packed屬性並重新排列了成員,因此較大的成員先於(即之前)較小的成員; 這可能會顯着減少填充。 這些函數允許您在uint8_t (您從線路發送/接收,或DMA ,或諸如此類)和test1結構之間進行轉換,因此這段代碼更加便攜 您可以從此代碼提供的有關協議結構的保證中受益,在此之前 - 就像它在實現的奇思妙想之前一樣,並且使用兩個不同實現的兩個設備可能不同意例如整數的內部表示。

您可以對所有索引進行硬編碼

    typedef __packed struct _test1
    {
        uint8_t a;
        uint32_t b;
        uint16_t c;
        uint16_t d;
        uint32_t e;
    } test1;

    enum
    {
        a = 0,
        b = 1,
        c = 5,
        d = 7,
        e = 9,
    };

    test1 t1 = {1,2,3,4};//not sure if init lists work for packed values
    printf("%u", *(uint32_t*)((uint8_t*)&t1 + b));

或者offsetof可以像這樣使用

printf("%u", *(uint32_t*)((uint8_t*)&t1 + offsetof(test1, b)));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM