简体   繁体   English

ARM GCC 打包结构警告地址

[英]ARM GCC Address of packed struct warning

In my code, I have something like this:在我的代码中,我有这样的东西:

#include <stdint.h>

typedef struct __attribute__((packed)) {
    uint8_t test1;
    uint16_t test2;
} test_struct_t;
test_struct_t test_struct;

int main(void)
{
    uint32_t *ptr = (uint32_t*) &test_struct;
    return 0;
}

When I compile this using arm-none-eabi-gcc, I get the warning当我使用 arm-none-eabi-gcc 编译它时,我收到警告

.\test.c:11:2: warning: converting a packed 'test_struct_t' pointer (alignment 1) to a 'uint32_t' {aka 'long unsigned int'} pointer (alignment 4) may result in an unaligned pointer value [-Waddress-of-packed-member] .\test.c:11:2:警告:将打包的“test_struct_t”指针(对齐 1)转换为“uint32_t”{又名“long unsigned int”}指针(对齐 4)可能会导致未对齐的指针值 [- Waddress-of-packed-member]

Can anyone tell me why this is happening?谁能告诉我为什么会这样? Taking the address of a packed struct member is of course dangerous.获取打包结构成员的地址当然是危险的。 But the whole struct itself should always be aligned, shouldn't it?但是整个结构本身应该总是对齐的,不是吗?

There is an answer in the comments, but since it's author didn't post it, I take the liberty to post it myself.评论里有答案,不过作者没发,我冒昧自己发一下。 All the credit is due to @Clifford.所有的功劳都归功于@Clifford。

By default, when the struct is packed, compilers also change alignment of the struct to 1 byte.默认情况下,打包结构时,编译器还会将结构的 alignment 更改为 1 个字节。 However, for your case you need the struct to be both packed and aligned as 32-bit unsigned integer. This can be done by changing the packing attribute as following:但是,对于您的情况,您需要将结构打包并对齐为 32 位无符号 integer。这可以通过如下更改打包属性来完成:

#include <stdint.h>

struct __attribute__((packed, aligned(sizeof(uint32_t)))) TestStruct {
    uint8_t test1;
    uint16_t test2;
};

struct TestStruct test_struct;

int32_t* p = (int32_t*)(&test_struct);

This compiles for ARM platform without any warnings.此编译适用于 ARM 平台,没有任何警告。

In my experience, "packed" structs are almost always a bad idea.根据我的经验,“打包”结构几乎总是一个坏主意。 They don't always do what people think they do, and they might do other things as well.他们并不总是做人们认为他们做的事情,他们也可能做其他事情。 Depending on compilers, target processors, options, etc., you might find the compiler generating code that uses multiple byte accesses to things that you expect to be 16-bit or 32-bit accesses.根据编译器、目标处理器、选项等,您可能会发现编译器生成的代码使用多字节访问来访问您希望是 16 位或 32 位访问的内容。

Hardware registers are always going to be properly aligned on a microcontroller.硬件寄存器总是会在微控制器上正确对齐。 (Bitfields may be a different matter, but you are not using bitfields here.) But there might be gaps or padding. (位域可能是另一回事,但您在这里没有使用位域。)但可能存在间隙或填充。

And the whole idea of trying to access this using a pointer to a uint32_t is wrong.尝试使用指向 uint32_t 的指针访问它的整个想法是错误的。 Don't access data via pointer casts like this - in fact, if you see a pointer cast at all, be highly suspicious.不要像这样通过指针转换访问数据 - 事实上,如果您看到指针转换,请高度怀疑。

So how do you get a structure that matches a hardware structure exactly?那么如何获得与硬件结构完全匹配的结构呢? You write it out explicitly, and you use compile-time checks to be sure:你明确地写出来,并使用编译时检查来确定:

#pragma GCC diagnostic error "-Wpadded"

struct TestStruct {
    uint8_t test1;
    uint8_t padding;
    uint16_t test2;
};

_Static_assert(sizeof(struct TestStruct) == 4, "Size check");

The padding is explicit.填充是显式的。 Any mistakes will be caught by the compiler.任何错误都会被编译器捕获。

What if you really, really want an unaligned 16-bit field in the middle here, and you haven't made a mistake in reading the datasheets?如果您真的非常想要中间有一个未对齐的 16 位字段,并且您在阅读数据表时没有犯错怎么办? Use bitfields:使用位域:

#pragma GCC diagnostic error "-Wpadded"

struct TestStruct2 {
    uint32_t test1 : 8;
    uint32_t test2 : 16;
    uint32_t padding : 8;
};

_Static_assert(sizeof(struct TestStruct2) == 4, "Size check");

Put the padding in explicitly.明确地放入填充。 Tell the compiler to complain about missing padding, and also check the size.告诉编译器抱怨缺少填充,并检查大小。 The compiler doesn't charge for the extra microsecond of work.编译器不对额外的微秒工作收费。

And what if you really, really , really need to access this as a uint32_t?如果您真的、真的真的需要将它作为 uint32_t 来访问怎么办? You use a union for type-punning (though not with C++):你使用一个联合来进行类型双关(虽然不是用 C++):

union TestUnion {
    uint32_t raw;
    struct TestStruct2 s;
};

Your packed structure has a size of 3 bytes, and there can be no padding in it.您的打包结构的大小为 3 个字节,其中不能有填充。 Thus, if we were to create an array of such structures, with the first element having a 4-byte aligned address then, by the definition of arrays (contiguous memory), the second element would be three bytes ( sizeof(struct test_struct_t) ) from that.因此,如果我们要创建此类结构的数组,第一个元素具有 4 字节对齐的地址,那么根据 arrays(连续内存)的定义,第二个元素将为三个字节sizeof(struct test_struct_t) )从那个。 Thus, the second element would have only single byte alignment – so, the alignment requirement of your structure is, by deduction, one byte .因此,第二个元素将只有一个字节alignment - 因此,通过推导,您的结构的 alignment 要求是一个字节

On your ARM platform, a unit32_t requires 4 byte alignment, hence the warning.在您的 ARM 平台上, unit32_t需要 4 个字节 alignment,因此出现警告。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM