Reputation: 365
Is it possible to initialise an array of uint8_t with a struct?
What I want to achieve is something similar to:
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
const uint8_t bytes[BIGGER_THAN_STRUCT] = (struct Device) {
.address = 123,
.id = 456,
};
The reason I want to do this it to get an easy overlay view of the content I write to the byte array. I simply want an easy interface to whatever first bytes I need for the information that is displayed by the structure.
If thats not possible, what's the closest thing to it?
Upvotes: 3
Views: 1574
Reputation: 70981
Beside approaching this via a union
(as proposed by hdante here https://stackoverflow.com/a/27462808/694576) instead of trying:
const uint8_t bytes[BIGGER_THAN_STRUCT] = (struct Device) {
.address = 123,
.id = 456,
};
do quick and dirty:
uint8_t bytes[BIGGER_THAN_STRUCT] = {0};
*((struct Device *) bytes) = ((struct Device) {
.address = 123,
.id = 456,
});
or better do:
struct Device dev = {
.address = 123,
.id = 456,
};
uint8_t bytes[BIGGER_THAN_STRUCT] = {0};
...
size_t size_dev = sizeof dev;
memcpy(bytes, &dev, size_dev);
Then inspect array bytes
up to the size_dev - 1
th element.
Upvotes: 1
Reputation: 8030
The standard way in C to overlay data types is by using unions:
#include <stdio.h>
#include <stdint.h>
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
union Memory {
uint8_t bytes[BIGGER_THAN_STRUCT];
struct Device devices[BIGGER_THAN_STRUCT/sizeof(struct Device)];
};
const union Memory memory = {
.devices = {
{ .address = 123, .id = 30 },
{ .address = 111, .id = 89 }
}
};
int main(void)
{
unsigned i;
for (i = 0; i < 16; i++)
printf("%d ", memory.bytes[i]);
putchar('\n');
return 0;
}
,
$ ./a
123 0 0 0 30 0 0 0 111 0 0 0 89 0 0 0
Upvotes: 3
Reputation: 84642
There is an alternative to using a uint8_t byte[]
array. You can also make use of a struct utilizing a bitfield
for each addr
and id
. You (may/may not) find it more convenient, but it does provide an easy way to keep the offset information associated with any give addr/id
pair.
I don't believe there is a way to directly make use of struct type Designated Initializers to fill the uint8_t byte
array. I think the closest full initialization would be with memcpy
. I've included that in the example below. Note, there is nothing that prevents you from filling the uint8_t byte
array with memcpy
, but then you have to track the offset
within the uint8_t byte
array to accurately point to any given byte in either addr
or id
for any given element. This is where the bitfield makes things a little easier. You get a one-to-one correlation between the struct Device
index and the uibitfield
index with a1..4
and b1..4
being the bytes within each addr
and id
, respectively.
A version using the uint8_t
array is shown below this version.
Here is a short example with test data in an array of struct Device
:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct /* bitfield corresponding to struct Device */
{
unsigned int a1 : 8,
a2 : 8,
a3 : 8,
a4 : 8;
unsigned int b1 : 8,
b2 : 8,
b3 : 8,
b4 : 8;
} uibitfield;
struct Device { /* original struct Device */
uint32_t addr;
uint32_t id;
};
int main () {
/* test data in an array of struct Device */
struct Device dev[] = { {0x4009f0, 0}, {0x4009f1, 1}, {0x4009f2, 2}, {0x4009f3, 3},
{0x4009f4, 4}, {0x4009f5, 5}, {0x4009f6, 6}, {0x4009f7, 7},
{0x4009f8, 8}, {0x4009f9, 9}, {0x4009fa, 10}, {0x4009fb, 11},
{0x4009fc, 12}, {0x4009fd, 13}, {0x4009fe, 14}, {0x4009ff, 15},
{0x400a00, 16}, {0x400a01, 17}, {0x400a02, 18}, {0x400a03, 19} };
int it = 0; /* general iterator */
size_t sz = sizeof (dev)/sizeof (*dev); /* size of array */
/* create validate and fill bitfield array */
uibitfield *bytes = calloc (sz, sizeof (*bytes));
if (!bytes) {
fprintf (stderr, "error: allocation failed.\n");
return 1;
}
memcpy (bytes, dev, sz * sizeof (dev));
/* print bytes in each addr & id in dev */
for (it = 0; it < sz; it++)
printf ("\n addr[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n id[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
it, (bytes + it)->a1, (bytes + it)->a2, (bytes + it)->a3, (bytes + it)->a4,
it, (bytes + it)->b1, (bytes + it)->b2, (bytes + it)->b3, (bytes + it)->b4);
printf ("\n");
return 0;
}
output:
$ ./bin/memview
addr[ 0]: 0xf0, 0x09, 0x40, 0x00
id[ 0]: 0x00, 0x00, 0x00, 0x00
addr[ 1]: 0xf1, 0x09, 0x40, 0x00
id[ 1]: 0x01, 0x00, 0x00, 0x00
addr[ 2]: 0xf2, 0x09, 0x40, 0x00
id[ 2]: 0x02, 0x00, 0x00, 0x00
addr[ 3]: 0xf3, 0x09, 0x40, 0x00
id[ 3]: 0x03, 0x00, 0x00, 0x00
addr[ 4]: 0xf4, 0x09, 0x40, 0x00
id[ 4]: 0x04, 0x00, 0x00, 0x00
(snip)
Note: it was a unclear how you would be using/filling struct Device
and how much of an initial peek you wanted at the data in stuct Device
, so this is just intended as an example of viewing the data.
using a uint8_t byte array:
If you do want to use the `uint8_t array, the changes needed are minimal:
/* using a uint8_t byte array */
uint8_t *bytearr = calloc (sz * 4, sizeof (*bytearr));
if (!bytearr) {
fprintf (stderr, "error: allocation failed.\n");
return 1;
}
memcpy (bytearr, dev, sz * sizeof (dev));
/* print bytes in each addr & id in dev using uint8_t array */
for (it = 0; it < sz * 4; it+=8)
printf ("\n addr[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n id[%2d]: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
it, bytearr[it], bytearr[it+1], bytearr[it+2], bytearr[it+3],
it, bytearr[it+4], bytearr[it+5], bytearr[it+6], bytearr[it+7]);
output is the same
Upvotes: 0
Reputation: 2239
Does this:
#define BIGGER_THAN_STRUCT 1024
struct Device {
uint32_t address;
uint32_t id;
};
struct DeviceAndData {
struct Device d;
char filler[BIGGER_THAN_STRUCT - sizeof(Device)];
};
const struct DeviceAndData bytes_pre = { .d = { .address = 123, .id = 456 } };
const uint8_t* bytes = (uint8_t*)&bytes_pre;
do the trick? :)
Upvotes: 0
Reputation: 1916
Usually, when I have to work with bag of structured bytes, I create a "view" struct/class that gives me some higher-level interface to a block of memory. Manual pointer arithmetic is usually too error prone to be repeated. Create a "memory view" structure and unit-test it properly.
struct memory_view {
uint32_t *addr;
uint32_t *id;
};
void view_init(struct memory_view *view, void *buf, size_t bufsz) {
// TODO: validate buffer size using bufsz here
view->addr = (uint32_t*)buf;
view->id = (uint32_t*)(buf + sizeof(uint32_t));
}
struct memory_view view;
uint8_t buffer[LARGE_NUMBER];
view_init(&view, buffer, LARGE_NUMBER);
*view->addr = 0xDEADBEEF;
*view->id = 0xCAFEBABE;
You can see a similar technique in device drivers, when structures are initialized to access different hardware registers located in some memory region.
You could also get a buffer pointer, cast it to structure and try using this memory block as it were a structure. Doable, but memory alignment can bite you hard. Such code may or may not work, depending on compiler and system architecture.
Upvotes: 0
Reputation: 1156
I think you maybe want to do something like that, even if the copy is not that necessary as b_sample
is exactly what you need.
#include <stdio.h>
#include <stdint.h>
typedef struct Device dev;
struct Device {
uint32_t address;
uint32_t id;
};
int main(void) {
//create an instance `sample.address=123` and `sample.id=456`
dev sample = (dev) { 123, 456 };
//convert dev pointer to byte pointer, so you loop through bytes
uint8_t* b_sample = (uint8_t *)(&sample);
//buffer for copy
uint8_t* bytes[1024];
int size = (int)(sizeof(dev)/sizeof(uint8_t)), i;
for(i = 0; i < size; i++) {
bytes[i] = b_sample[i];
//see what values you copy
printf("%x ", bytes[i]);
}
return 0;
}
Demo: http://codepad.org/wE8dbBV1
If you want to divide the struct into uint16_t
segments you can safely replace all uint8_t
with uint16_t
Upvotes: 0
Reputation: 53026
This will do it then
static const uint8_t buffer[BIGGER_THAN_STRUCT] = {
0x7b, 0x00, 0x00, 0x00,
0xc8, 0x01, 0x00, 0x00
};
Upvotes: 0