简体   繁体   English

如何在没有未对齐模式的情况下访问打包结构中的变量?

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

I'm using packed structure for communication using direct DMA access, and here is my test code: 我正在使用打包结构进行直接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;

size of these structures are 这些结构的大小是

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

if I want to access variable b, for not effecting performance, read/write memory content with aligned access is needed, with calculated offset by hand 如果我想访问变量b,为了不影响性能,需要具有对齐访问权限的读/写内存内容,手动计算偏移量

t3.buf[1]

but I cannot read/write variables in structure without using unaligned accesses 但是我不能在不使用未对齐访问的情况下在结构中读/写变量

t2.t.b
t3.t.b

so I defined structures like the following code, packed only variable a 所以我定义了如下代码的结构,只包含变量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;

although access of all element in structure is aligned, but padding is inserted either 尽管对齐了结构中所有元素的访问,但也插入了填充

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

so how can I define packed structures, and access single variable in it without using unaligned access(except a)? 那么如何定义打包结构,并在不使用未对齐访问的情况下访问其中的单个变量(除了a)?

thanks a lot for helping 非常感谢你的帮助

Your question introduces two problems under the umbrella of one: 你的问题引入了两个问题:

  • Communication between components and/or devices; 组件和/或设备之间的通信; this may or may not have the same underlying representation of structures and integers, hence your use of the non-portable __packed attribute. 这可能有也可能没有结构和整数的相同底层表示,因此您使用了非可移植的__packed属性。
  • Performance of access, biased by alignment and/or data size; 访问性能,对齐和/或数据大小的偏差; on one hand the compiler aligns data to fall in line with the bus, yet on the other hand that data might occupy too much space in your cache. 一方面,编译器将数据对齐以与总线一致,但另一方面,数据可能占用缓存中的太多空间。

One of these is the actual problem you want to solve, X, and the other the Y in your XY problem . 其中一个是您要解决的实际问题,X,另一个是XY问题中的Y. Please avoid asking XY problems in the future. 请避免将来问XY问题。

Have you considered how to guarantee that uint16_t and uint32_t will be big endian or little endian, based on your requirements? 你有没有考虑过如何根据你的要求保证uint16_tuint32_t是big endian还是little endian? You need to specify that, if you care about portability. 如果您关心可移植性,则需要指定。 I care about portability, so that's what my answer will focus on. 我关心可移植性,所以这就是我的答案所关注的。 You may also notice how optimal efficiency will be obtained, too. 您可能还会注意到如何获得最佳效率。 Nonetheless, to make this portable: 尽管如此,要使这个便携:

  • You should be serialising your data using serialisation functions to convert each member of your structure into sequences of bytes by division and modulo (or left shift and binary and ) operations. 应该使用序列化函数对数据进行序列化,以通过除法 (或左移二进制和 )运算将结构的每个成员转换为字节序列。
  • Similarly, you should be deserialising your data by inverse operations multiplication and addition (or right shift and binary or ). 同样,您应该通过逆运算乘法加法 (或右移二进制或 )来反序列化您的数据。

As an example, here's some code showing both little endian and big endian for serialising and deserialising test1 : 举个例子,这里有一些代码显示了用于序列化和反序列化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);
}

You may notice that I removed the __packed attribute and rearranged the members, so that the larger members precede (ie come before) the smaller; 您可能会注意到我删除了__packed属性并重新排列了成员,因此较大的成员先于(即之前)较小的成员; this is likely to reduce padding significantly. 这可能会显着减少填充。 The functions allow you to convert between an array of uint8_t (which you send to/receive from the wire , or DMA , or whatnot) and your test1 structure, so this code is much more portable . 这些函数允许您在uint8_t (您从线路发送/接收,或DMA ,或诸如此类)和test1结构之间进行转换,因此这段代码更加便携 You benefit from the guarantees this code provides regarding the structure of your protocol, where-as before it was at the whim of the implementation, and two devices using two different implementations might have disagreed about the internal representation of integers for example. 您可以从此代码提供的有关协议结构的保证中受益,在此之前 - 就像它在实现的奇思妙想之前一样,并且使用两个不同实现的两个设备可能不同意例如整数的内部表示。

You could hard code all the indexes like 您可以对所有索引进行硬编码

    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));

Or offsetof can be used like this 或者offsetof可以像这样使用

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我如何确定在32位和64位系统上都打包有此C结构? 是否总是需要“ __attribute __((包装))”? - How can I be sure that this C structure is packed both on 32 bit and 64 bit systems? Is “__attribute__((packed))” always necessary? 如何在不使用#pragma pack或__atribute __((packed))的情况下通过结构指针读取BMP文件并访问其头信息? - How can I read a BMP file and access its header info via a struct pointer without using #pragma pack or __atribute__((packed))? 如何捕获未对齐的内存访问? - How to trap unaligned memory access? 我们可以将非打包结构元素添加到打包结构中吗 - Can we add non-Packed structure element to Packed structure 打包的C结构和函数可以移植到Java吗? - Can a packed C structure and function be ported to java? 如何使用C语言初始化和访问此结构? - How can I initialize and access this structure in C language? 如何从该结构正确访问指针? - How can I properly access the pointers from this structure? 如何在运行时按名称访问结构字段? - How can I access structure fields by name at run time? 使用memcpy进行未对齐访问 - unaligned access with memcpy 如何在结构中保存的字符串中访问单个字符,而我只有指向此结构的指针 - How can I get access to single chars in strings that are saved in structure while I only have pointer to this structure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM