-1

On gcc/clang, long double use 10 bytes but take 16 bytes. It'd be good if this struct is 16 bytes but it's actually 32 bytes, wasting half of bandwidth.

struct {
    long double x;
    int id;
};

I tried to union two values, but since compiler assumes the whole 16 bytes used only by the long double, editing it sometimes modifies int.

Any good solution to pack them finely?

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
l4m2
  • 1,157
  • 5
  • 17

2 Answers2

1

On gcc/clang, long double use 10 bytes but take 16 bytes.

I think you mean that the storage size of long double is 16 bytes, of which only 10 are significant to its numeric value (so 6 are padding).

This would be primarily a property of the target ABI for which the compiler is building, not of the compilers themselves.

It'd be good if this struct is 16 bytes but it's actually 32 bytes, wasting half of bandwidth.

Have you considered using double instead of long double?

In any case, the union is the only way that C provides for two or more live objects to have overlapping storage, where neither object comprises the other. That does not serve the purpose of storing two overlapping values simultaneously. Suppose you knew that the first 10 bytes of a long double were the significant ones, and that the size of int is 4. You might then imagine doing something like this:

union problemmatic {
    long double x;
    struct {
        char padding[12];
        int id;
    }
};

But

  • it is not safe to assume that writing to member x writes only the 10 significant bytes, leaving the others alone, and
  • it is not safe to assume that writing to x and then writing to id does not cause the stored value of x to be a trap representation

That is, writing to either member could invalidate the other.

Bottom line: if you want to use less storage then use smaller data types (i.e. double rather than long double). If you're committed to the data types you're using now then their storage footprint is part of the package.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • you can do a manual `memcpy` to copy only the 6 significant bytes to avoid the possibility that all the 16 bytes were written when the `long double` was accessed – phuclv Jun 29 '23 at 14:26
  • Sure, @phuclv, provided that you know which bytes of the `long double` you need to preserve, and that you know a valid way to set the others, you can manually pack the significant bytes of a `long double` and of an `int` into 16 bytes of storage, and unpack them later. But that's not at all analogous to the OP's `struct`. And depending on how these data are to be used, it may be even worse for memory, because every time you want to access the `long double`, you need at least a separate, 16-byte staging space for copying it in or out. – John Bollinger Jun 29 '23 at 14:32
-1
union {
    long double x;
    struct {
        int reserved[3];
        int id;
    };
};
Ziming Song
  • 1,176
  • 1
  • 9
  • 22
  • See the godbolt link, it sometimes fail – l4m2 Jun 29 '23 at 10:53
  • 2
    Dirty. It depends on how the compiler / architecture places the `long double` inside the 16 bytes. Also, you depend on `sizeof(int)*3>=10`, which may not be the case. Better use `unsigned char reserved[10]` or `uint8_t reserved[10]`. General recommendation: Don't do it. – 12431234123412341234123 Jun 29 '23 at 10:58
  • 1
    Generally, it is undefined behaviour to read a value that was not the value written last. You cannot legally use both fields of a union separately. – Gerhardh Jun 29 '23 at 10:58
  • @12431234123412341234123 Dirty is unavoidable we're assuming `long double` take 10 bytes – l4m2 Jun 29 '23 at 10:59
  • You can't do that: 1)padding is compiler dependent, 2)members ordering inside a struct is compiler dependent. In the best case the code isn't portable. – Frankie_C Jun 29 '23 at 11:09
  • @Frankie_C The compiler is not allowed to change the order of the members. But the compiler may add different amount of padding (but it has to be the same on one system, otherwise some libraries would break). – 12431234123412341234123 Jun 29 '23 at 11:12
  • 3
    @Gerhardh: Re “Generally, it is undefined behaviour to read a value that was not the value written last”: No, it is not. Per C 2018 6.5.2.3 3, the `.` and `->` operators provide the value of the named member, and the standard does not state a restriction that the member must be the last one written, and note 99 makes it clear this lack of a restriction is intentional and that the bytes will be reinterpreted as the new member type. Attempting to overlap members as OP desires will encounter problems due to the bytes not being set as they desired, but not due to undefined behavior from overlap. – Eric Postpischil Jun 29 '23 at 11:37
  • 3
    @Frankie_C: Re “members ordering inside a struct is compiler dependent”: The members of a structure must be allocated in the order they are declared. There may be padding between them, but they cannot be reordered. Per C 2018 6.5.8 5, a pointer to a structure member must have a greater address than a pointer to another member of the same structure declared earlier in the structure. – Eric Postpischil Jun 29 '23 at 11:44
  • The lack of explanation makes this a horrible answer. (Also, int[3] instead of char[12] is weird.) – ikegami Jun 29 '23 at 13:38