0

I have a buffer that is N bytes long, where N is at least 50. This buffer is in a very specific format, where the first 4 bytes are a unsigned int representing how long the buffer is (including those four bytes). The next 16 bytes are four unsigned ints with a different meaning. The next N-20 bytes are some general purpose buffer of memory.

Right now, to set this buffer up, I do something like this:

memcpy((char*)my_buf + 0, &buf_size, 4);    //buf_size is some unsigned int
memcpy((char*)my_buf + 4, &prop0, 4);    //buf_size is some unsigned int
memcpy((char*)my_buf + 8, &prop1, 4);    //buf_size is some unsigned int
memcpy((char*)my_buf + 12, &prop2, 4);    //buf_size is some unsigned int
memcpy((char*)my_buf + 16, &prop3, 4);    //buf_size is some unsigned int
//Many more bytes used as a generic buffer

However, this seems very inelegant. At this point I would like to ask the community if there is a more elegant way of assigning particular values to particular memory offsets when given an initial address.

I thought about using a struct, but IIRC a struct does not guarantee which order its members are placed in memory, and I don't know how a struct could represent the tail of the buffer which is generic memory space.

Adam S
  • 8,945
  • 17
  • 67
  • 103
  • Are you guaranteed that an `unsigned int` will always be four bytes? – Alex Reynolds Oct 31 '12 at 21:20
  • 0) this is an excellent method. 1) remove the casts, they are not needed. 2) add an `assert(sizeof(unsigned int) == 4);` or use sizeof instead of the hardcoded 4. [or use a struct that contains the wanted offsets.+sizes] – wildplasser Oct 31 '12 at 21:33
  • I prefer to use `long` instead of `int` when I absolutely require 32-bit integers. – paddy Oct 31 '12 at 21:45
  • Has anyone had a struct that was not laid out how they specified it to be? I never have. If that was ever the case I'd lose my trust in C. –  Nov 01 '12 at 01:57

3 Answers3

1

a struct does not guarantee which order its members are placed in memory

That's false. The order is guaranteed. Eventually the compiler can introduce some padding. But you are working with multiple of 4-bytes so padding shouldn't happen (at least in a 32-bit architecture, what's your?).

Have a look at this thread, particularly the citations to both C and C++ standard.

Community
  • 1
  • 1
Heisenbug
  • 38,762
  • 28
  • 132
  • 190
  • Right. packing struct members is usually a compiler option, padding often the default. – ARA Oct 31 '12 at 21:52
1

You should at least use sizeof(unsigned int) instead of 4 or multiple of 4s:

memcpy((char*)my_buf + 0, &buf_size, sizeof(unsigned int));
memcpy((char*)my_buf + 1 * sizeof(unsigned int), &prop0, sizeof(unsigned int));
memcpy((char*)my_buf + 2 * sizeof(unsigned int), &prop1, sizeof(unsigned int));
memcpy((char*)my_buf + 3 * sizeof(unsigned int), &prop2, sizeof(unsigned int));
memcpy((char*)my_buf + 4 * sizeof(unsigned int), &prop3, sizeof(unsigned int));

If your memory is organized in multiples of unsigned ints, you can also use an array of unsigned int:

unsigned int my_buf[X];

my_buf[0] = buf_size; // better yet, use sizeof(my_buf)!
my_buf[1] = prop0;
my_buf[2] = prop1;
my_buf[3] = prop2;
my_buf[4] = prop3;

or use a struct as others already pointed out.

Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
0

Create your struct and trust that the memory is laid out as you say.

The last field in your struct can be

struct MyStruct {

   // your fields above here...
   char generic[];
};

When you create your struct instance, you'll need to know the size of the generic memory.

MyStruct* instance = malloc(sizeof(MyStruct) + genericBytes);

To access the generic portion use the generic field.

instance->generic[i] = something;

@Heisenbug is right, but if padding becomes a concern use the pad pragma before and after the struct definition.

#pragma pad(1)
// ... your struct
#pragma pad()
Jason
  • 1,612
  • 16
  • 23
  • In this case, won't "char generic[]" be a char*? In other words it will be a pointer to some other memory? – Adam S Oct 31 '12 at 21:29
  • It's an alias to the memory that doesn't take any space in the struct. – Jason Oct 31 '12 at 21:30
  • Thanks. In Visual Studio, I get this warning: warning C4200: nonstandard extension used : zero-sized array in struct/union. Although it would work, it seems this is not "ideal" C code. – Adam S Oct 31 '12 at 21:39
  • I've used this trick with Visual Studio and it seems safe. You can give the array a size of 1, but then subtract that from the calculation for the total memory size. In fact, that's an easier way to understand why generic points into the structure's memory. – Jason Oct 31 '12 at 21:42
  • Unfortunately in my case, I cannot have any warnings or something like char generic[1]. Thank you for the partial solution, however. – Adam S Oct 31 '12 at 21:46