c - casting uint8_t* to uint32_t* behaviour

断了今生、忘了曾经 提交于 2021-01-28 02:09:56

问题


I have read this question: How does casting uint8* to uint32* work? but I am unsure of the answer given.

I'm newbie embedded C programmer working on an project that uses GCC and I've been refactoring chunks of code to reduce the memory usage. An example is where I changed the data types of some variables from uint32_t to smaller sized types:

uint8_t colour;
uint16_t count;
uint16_t pixel;

func((uint32_t*)&colour);
func((uint32_t*)&count);
func((uint32_t*)&pixel);

Where func(uint32_t* ptr) modifies the value passed in to data it receives on a slave port. I'm coming across a problem where the above code does not behave correctly when -O1, -O2, -O3 or -Os optimisation is enabled. E.g when values 1 5 1 are received on the comms port and the function respectively, the value that is set for the variables are 0 0 1. When no optimisation is enabled, the values are set correctly.

The code behaves correctly if I change the data types back to uint32_t. I don't understand why I don't get any warnings from the compiler (I have extra warnings turned on). Is the reason this is happening to do with the endianess/allignment?

What are the pitfalls of upcasting from a uint8_t pointer to a uint32_t pointer?


回答1:


TLDR

Doing something like passing the address of a uint8_t to something expecting the address of a uint32_t can result in corrupted memory, unknown results, subtle bugs, and code that just plain blows up.

DETAILS

First, if the function is declared as

void func( uint32_t * arg );

and modifies the data arg points to, passing it the address of a uint8_t or a uint16_t will lead to undefined behavior and likely data corruption - if it runs at all (keep reading...). The function will modify data that is not actually part of the object the pointer passed to the function refers to.

The function expected to have access to the four bytes of uint32_t but you gave it the address of only a single uint8_t bytes. Where do the other three bytes go? They likely stomp on something else.

And even if the function only reads the memory and doesn't modify it, you don't know what's in the memory not in the actual object, so the function may behave unpredictably. And the read might not even work at all (keep reading again...).

Additionally, casting the address of a uint8_t is a strict aliasing violation. See What is the strict aliasing rule?. To summarize that, in C you can not safely refer to an object as something that it is not, with the only exception being that you can refer to any object as if it were composed of the proper number of [signed|unsigned] char bytes.

But casting a uint8_t address to a uint32 * means you are trying to access a set of four unsigned char values (assuming uint8_t is actually unsigned char, which is almost certainly true nowadays) as a single uint32_t object, and that's a strict aliasing violation, undefined behavior, and not safe.

The symptoms you see from violating the strict aliasing rule can be subtle and really hard to find and fix. See gcc, strict-aliasing, and horror stories for some, well, horror stories.

Additionally, if you refer to an object as something it's not, you can run afoul of 6.3.2.3 Pointers, paragraph 7 of the C (C11) standard:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

It's not safe even on x86 no matter what someone might tell you about x86-based systems.

If you ever hear someone say, "Well, it works so that's all wrong.", well, they're very, very wrong.

They just haven't observed it failing.

Yet.



来源:https://stackoverflow.com/questions/60890346/c-casting-uint8-t-to-uint32-t-behaviour

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!