cancel
Showing results for 
Search instead for 
Did you mean: 

C / C++ memory layout and optimization, problem with type punning, loading and saving.

HTD
Senior III

I have some data as static class members. I use `__attribute__((__packed__))` for some classes and structs. I use type punning to convert between types that I believe have the same memory layout, but different signatures (members of different translation units). It works. I assume the sequence and offsets of members is as in my class / struct definition. Knowing this I just save and load memory regions to and from files and it works.

Until I enable any optimization in the compiler. I noticed that when my configuration file was written by the debug version, it does not load in release version. The first member is loaded correctly, the subsequent ones are not.

My only guess here it can be related to a different memory layout using -O1 .. -O3. Can `__attribute__((__packed__))` be ignored when optimizations enabled? Is there a way to express in my code I want a specific memory layout for a structure?

When I have a class that has only static members, is it equivalent of a C struct in memory? When the class is marked with the attribute, does it affect its static members? Is it a way to ensure specific members are always put in specific relative memory locations?

Consider the code:

#include "stdint.h"
 
class __attribute__((__packed__)) X
{
public:
    static uint8_t a;
    static uint16_t b;
    static uint32_t c;
};
 
uint8_t X:a;
uint16_t X:b;
uint32_t X:c;
 
struct __attribute__((__packed__)) Y
{
    uint8_t a;
    uint16_t b;
    uint32_t c;
};
 
int main(void)
{
    Y s;
    s.a = 1;
    s.b = 2;
    s.c = 3;
    memcpy((void*)X.a, &s, sizeof(Y));
    bool ok = s.a == X.a && s.b == X.b && s.c == X.c;
}

When compiled with -O0, the `ok` is true. When compiled with -O1 - the `ok` is false.

What am I missing here?

12 REPLIES 12
HTD
Senior III

Oh, another typo. I was typing it "live" in the post, not copying from actual code. I rewritten it in an actual example later to test how it behaves. What was the use? I guess laziness, less typing when accessing the members later. Sometimes it just works fine, like you have just one entity that has some properties, that's probably the shortest and simplest way to code it. Beside using plain old C with a namespace that has its pros and cons. The "pro" is readability: like "Car::speed" is IMO more readable than "Car::data->speed" or "Car::data->speed()" or "Car::instance().speed". By doing some weird experiments I learned some interesting things. When I started with C++ it was like there are too many ways of doing the same thing, then I learned practically why we choose specific ways over the others. Yep, you can read all about it, but I learn better just doing stuff (a matter of personal preference). BTW, that `Settings` entity was originally a namespace, the "properties" was a struct. Then I tried to convert it into a "static class" and made a mistake of not leaving the original data structure alone. The catch was it worked in debug builds 😉 It's fixed now and tested with all -Ox options. In the process I found other bugs like using a null pointer as if it was valid 😉 Surprisingly - it can also work - hiding the hideous bug.

>  unlike __attribute__((packed)), it's valid in MSVC

IIRC the Visual C++ once had a clang front-end (experimental). If it is still available, it could help with gcc compatibility.

Pavel A.
Evangelist III

Of course. 0 is a valid memory address for some STM32s.