[英]How can I be sure that this C structure is packed both on 32 bit and 64 bit systems? Is “__attribute__((packed))” always necessary?
[英]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_t
和uint32_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.