1

I have an object that writes to a file using classic fwrite, fseek, and so on. It writes to files using structs, such as

fwrite(&my_struct, 1, sizeof(MyStruct_t), fd);

I did not write this code but that has been working for ~15 years. I have a requirement to convert this two objects, one that continues to write to a file in this way, and now to write to a vector to avoid the file i/o. So I replaced all fwrite, fread, etc, with Write and Read and so on. In the file writing object this simply wraps the file functions, and for the vector object this simply pushes data into the vector or moves a local vector_position around to best maintain transparency between the two objects.

However, "writing" (and likely reading) from the vector is not working out as I hoped because of, I suppose, data alignment issues. I'm not even sure if this would be a problem in my OS (vxWorks) because I see all structs have some data alignment macro. But I would like to know if there is a non macro way to go about this.

The issue is that, I naively expected sizeof m here to be 5 while it is actually 8 causing me to push garbage into my vector. Is there any way to do this without aligning the struct based on my compiler? I write most of my prototypes in an online environment.

#include <iostream>
#include <string>
#include <vector>

typedef struct {
    uint32_t a;
    char b;
} myst;

void Write(std::vector<unsigned char> &v, void *data, size_t s){
    unsigned char *bdata = static_cast<unsigned char*>(data);
    for(size_t i = 0; i < s; i++){
        printf("0x%x\n", bdata[i]);
        v.push_back(bdata[i]);  
        // problem - "s" = 8, not 5, so more data is written 
    }
}

int main()
{
    myst m;
    m.a = 0xA1B2C3D4;
    m.b = 0xE5;
    std::vector<unsigned char> s;
    Write(s, &m, sizeof(m));
}
Christian Gibbons
  • 4,272
  • 1
  • 16
  • 29
  • 3
    I think this answer might make you understand more: [SO-Answer](https://stackoverflow.com/a/119128/11023871) Basically, your integer takes 4 bytes, your char takes 1 byte, but your system will do a padding of 3 bytes to fill the remaining to 8 – David Buzatu Jul 21 '20 at 16:25
  • 1
    Push each item individually. – john Jul 21 '20 at 16:26
  • 1
    But your code with fread and fwrite will have the same issue, so is it really a problem? – john Jul 21 '20 at 16:27
  • Your question seems to be whether there's a standardised way to pack, avoiding padding, and the answer is there is not (because that's usually implemented as a non-standard `#pragma`) – underscore_d Jul 21 '20 at 16:32
  • Does this answer your question? [Is there any way to control the padding between struct members (incl. bit field) in C++?](https://stackoverflow.com/questions/18653110/is-there-any-way-to-control-the-padding-between-struct-members-incl-bit-field) – underscore_d Jul 21 '20 at 16:35
  • @john You're right. Each struct is currently manually aligned and I suppose I understand the reason for doing this was to make `fread` and `fwrite` easier to use in this context. –  Jul 21 '20 at 16:40
  • Your struct wastes 3 bytes to alignment, snd so does its byte-for-byte copy to a file, and a byte-for-byte copy to a vector. No big surprise here, and also no harm whatsoever. I recommend leaving this thing alone. – n. m. could be an AI Jul 21 '20 at 17:33
  • On a different note, I don't see any purpose of hex-formatting your bytes. You don't do that when you write to a file. Why do that when writing to a vector? Also you may want to use `std::stream` facilities rather than your homegrown solution, and avoid a bunch of totally unnecessary code duplication. – n. m. could be an AI Jul 21 '20 at 17:36

2 Answers2

1

To force it down to the unpadded size you can use

#pragma pack(1)

As described in GNU docs in https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html it "simply sets the new alignment" to the value in between parenthesis.

lerv22
  • 11
  • 1
  • 2
0

In case your struct ends up in an array, sizeof needs to add the extra padding in the end. I don't think there's a way around this. If your problem is "to push garbage into my vector", my advice is to simply initialize the struct to zero before using it:

myst m;
memset(&m, 0, sizeof(m));
m.a = 0xA1B2C3D4;
m.b = 0xE5;

This is very common in the embedded space (VxWorks).

eltricos
  • 18
  • 4