A cast that is breaking strict-aliasing rules
Asked Answered
C

3

6

I have a function that takes a unsigned long* and needs to pass it to a external library that takes a unsigned int* and on this platform unsigned int/long are the same size.

void UpdateVar(unsigned long* var) {
   // this function will change the value at the address of var
   ExternalLibAtomicUpdateVar((unsigned int*)var); // lib atomically updates variable
}

This generate a warning saying that its breaking strict-aliasing rules. Are there any work arounds?

Thank you

Edit: I apologize for not being clear. The code is an atomic update so going around the library to store it is not an option. I could drop down to assembly but I'd like to do this in C++.

Chippewa answered 21/9, 2010 at 19:40 Comment(2)
just for a single object or an array of longs/ints ?Historicity
It's an atomic store to a single 32 bit piece of memory.Chippewa
S
8
void UpdateVar(unsigned long* var) {
   unsigned int x = static_cast<unsigned int>(*var);
   ExternalLibUpdateVar(&x);
   *var = static_cast<unsigned long>(x);
}
Serbocroatian answered 21/9, 2010 at 19:44 Comment(6)
Sorry; didn't notice the C++ tagSerbocroatian
What is the real difference between static_cast and reinterpret_cast in this case?Sedum
Now that's something different. Imagin that ExternalLibUpdate stores the passed address somewhere. It would store the address of the local x, not the address in var.Oriental
@IceDane I believe the behavior of reinterpret_cast here would be undefined -- as far as I know the only thing that's guaranteed is if you reinterpret_cast to a larger type then back again you'll get the original value backSerbocroatian
I edited my original post to reflect that the external lib is an atomic update so I'm not allowed to go around it to store it myself.Chippewa
You should properly @address people in comment replies, otherwise they don't see your replies in their responses tab. I only came by here by accident. Anyway, up-voted now.Flanagan
S
2

This should work:

void UpdateVar(unsigned long* var) {
   // this function will change the value at the address of var
   ExternalLibUpdateVar(reinterpret_cast<unsigned int*>(var));
}
Sedum answered 21/9, 2010 at 19:43 Comment(6)
dangerous if sizeof(long) != sizeof(int)Electrical
He said in the original post that they were guaranteed to be the same size on his platform. But yes, you are right.Sedum
+1. Under certain conditions, this is actually appropriate in my opinion. But it really depends on the case and I'd feel more comfortable with some sort of sizeof(int)==sizeof(long) check in the program that makes compilation fail in case the condition is not met -- C++0x's static_cast anyone? :-)Historicity
I use a macro like this to do static asserts: #define static_assert(x) extern char static_assert[(x) ? 1 : -1]Serbocroatian
I do a static_assert in my code but I don't see how this fixes the aliasing problem.Chippewa
you're asking how to remove the warning, right? The reinterpret_cast will do that. A static_cast would as well. You're telling the compiler that you know what you're doing and to not bug you about it.Coalition
M
1

Nothing in the C Standard mandates that int and long must have the same size; further, even if they do have the same size, nothing in the Standard mandates that they have the same representation (among other things, they could have incompatible combinations of padding bits and trap representations, such that aliasing between the two types could not serve any useful purpose).

The authors of the Standard did not want to force implementers targeting platforms where aliasing between int and long would serve no purpose to recognize such aliasing. They also did not want to write rules which would be applicable to some platforms (those where aliasing would serve a purpose) but not others (those where it wouldn't). Instead, they figured that people writing quality compilers would try to recognize aliasing in cases where it was useful.

Being able to use pointers to one 32-bit type to read and write values of another 32-bit type that has the same representation is clearly useful, especially if APIs are split as to which type they expect. If some commonplace APIs on a platform use int* for 32-bit values and others use long*, a quality general-purpose implementation for that platform must allow data of either type to be accessed using pointers of the other.

Unfortunately, however, the authors of some compilers are more interested in processing a certain subset of programs quickly, than in processing a larger subset of programs usefully, and cannot be relied upon to generate useful code if it's necessary to exchange data between APIs that use the same data representation but different named types unless one completely disables aliasing analysis. Of course, if one is targeting dialects of C that are suitable for general-purpose use on microcontrollers, such issues don't matter.

Micrometer answered 19/8, 2016 at 20:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.