2

I want to refer to a memory location as either an array of chars or a specialized struct with two upfront char members. The reason for this is a C-API that only takes a char array, but I want to put some more information into it with my struct that I can then read out based on whether the first character is a special character (e.g. "@") or not.

struct some_struct {
   char a;
   char b;
   void* some_ptr;
}

However here's the problem: As far as I know, char arrays are not padded. Structs however are aligned by the size of their largest member according to this topic. When I want to refer to the first two chars by either the struct or the char array, it doesn't add up:

Front padded struct compared to char

Am I right to assume this is how the struct is aligned compared to the array (4 byte alignement)?. If so, how can I remove the unwanted front padding so that the first two chars of the struct line up with the char array? The rest of the struct can be default aligned to 4 byte boundaries, because I do not want to #pragma pack(1) the whole thing.

EDIT: Here's my intended usage:

  • Create struct some_struct
  • reinterpret_cast it to const char *
  • pass it to C API as a const char *
user3840170
  • 26,597
  • 4
  • 30
  • 62
glades
  • 3,778
  • 1
  • 12
  • 34
  • What's your struct declaration? Is it just two char members? – Gaurav Sehgal Apr 12 '22 at 06:29
  • 2
    You use C or C++? Decide. – Raildex Apr 12 '22 at 06:29
  • 1
    The issue of aligning occurs when you **allocate**. When you have an array of chars, they are not your struct. When you allocate a struct, it's not an array of chars. – Raildex Apr 12 '22 at 06:30
  • It looks more like a struct is allocated on an even address, whereas the char array on any address. That would not bode well. – Joop Eggen Apr 12 '22 at 06:31
  • Why not have a dummy `char` member first in the structure for the unwanted `char[0]` of the array? Same with other unwanted data? You can add dummy byte-size members (or arrays of bytes) anywhere in a structure to control padding yourself. – Some programmer dude Apr 12 '22 at 06:31
  • 3
    There shouldn't be any padding at the beginning. https://stackoverflow.com/questions/55560382/struct-packing-how-to-add-struct-members-at-the-beginning https://stackoverflow.com/questions/8864311/pods-and-inheritance-in-c11-does-the-address-of-the-struct-address-of-the – Retired Ninja Apr 12 '22 at 06:31
  • alignment of struct is platform-defined, although that's the most common way. Ability to work with non-aligned members and structures is too, so `pragma pack(1)` isn't portable solution anyway. – Swift - Friday Pie Apr 12 '22 at 06:34
  • https://en.cppreference.com/w/cpp/language/alignas – Klaus Apr 12 '22 at 06:41
  • So, you want to do `char buffer[SIZE]; buffer[0] = '@'; auto mystruct = reinterpret_cast(&buffer[1]);`? That would be consistent with your picture. – user3840170 Apr 12 '22 at 06:54
  • @GauravSehgal I added struct info in description – glades Apr 12 '22 at 06:56
  • @Raildex: C++ but does it matter? I do not understand your second comment – glades Apr 12 '22 at 06:56
  • @JoopEggen: That's exactly my problem. But can I not force the compiler to lay out my struct to uneven addresses just like chars are layed out? – glades Apr 12 '22 at 06:57
  • @Someprogrammerdude: The first char is not unwanted, I just assume it starts at struct alignement (= strictest alignement = pointer alignement = 4 bytes) which is what I don't like. – glades Apr 12 '22 at 06:59
  • @RetiredNinja: Padding maybe isn't the right word on second thought. Alignement probably fits better. I want to get rid of the alignement of the first struct members essentially – glades Apr 12 '22 at 07:00
  • @Swift-FridayPie: It doesn't have to be a portable solution – glades Apr 12 '22 at 07:01
  • I don't understand? in your usage there is no unwanted padding. the result of `reinterpret_cast` would point to `some_struct::a` – apple apple Apr 12 '22 at 07:29

1 Answers1

1

I think the simplest way to accomplish what you (seem to) want is to incorporate the prefix intrusively into your data structure. In other words, declare it the following way:

struct some_struct {
    char magic;  /* must be '@' */
    char a;
    char b;
    void *some_ptr;
};

and then do this to actually initialize it:

#include <new> // for placement new

alignas(some_struct) char buffer[SIZE];
static_assert(sizeof(buffer) >= sizeof(some_struct));

auto mystruct = new (buffer) some_struct;
mystruct->magic = '@';
/* fill the other fields of *mystruct */

The resulting memory layout will be this:

buffer:
┌───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┬╌╌╌
│  [0]  │  [1]  │  [2]  │  [3]  │  [4]  │  [5]  │  [6]  │  [7]  │
└───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┴╌╌╌

*mystruct:
┌───────┬───────┬───────┬───────┬───────────────────────────────┐
│ magic │   a   │   b   │ (pad) │            some_ptr           │
└───────┴───────┴───────┴───────┴───────────────────────────────┘

making &mystruct->some_ptr well-aligned, as long as buffer is sufficiently aligned to store a some_struct.

user3840170
  • 26,597
  • 4
  • 30
  • 62
  • In your answer you align the char array to `some_struct`'s boundaries. Could you do the opposite and create`some_struct` aligned to char boundaries like `alignas(char) some_struct mystruct;` ? Am I correct that this would also get rid of first member alignement requirement? – glades Apr 12 '22 at 07:23
  • A structure is always aligned to `char` boundaries, because `char` has an alignment requirement of 1, which everything trivially satisfies. If you want to set the alignment requirement of `some_struct` to 1 (to make it storable in any byte buffer), there is no standard way to do that, but you can apply a packing pragma (like you mentioned) or an attribute like `[[gnu::packed]]`. [Beware of UB in that case, however.](https://stackoverflow.com/q/8568432/3840170) – user3840170 Apr 12 '22 at 07:53
  • Alright thanks. I have to get used to the right terminology it seems. Yes, the alignement requirement of the struct should be 1 byte but the padding should not be affected such as in [[gnu::packed]]. But I guess that's asking for too much. I figured I could also just reinterpret cast the struct to char and then it will automatically fullfill alignements of the struct. – glades Apr 12 '22 at 09:34
  • `auto mystruct = reinterpret_cast(buffer);` is a [strict aliasing violation](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) and therefore undefined behavior. Seems as if the details of the C api in question might be troublesome. – Andrew Henle Apr 12 '22 at 09:58
  • @AndrewHenle Not quite, character pointers are allowed to alias pointers to other objects just fine. (It’s how `memcpy` manages not to be instant UB.) – user3840170 Apr 15 '22 at 07:26