Initialize C array with structure

Is it possible to initialize a uint8_t array with a structure?

What I want to achieve is 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 is to get an easy overlay view of the content I am writing to the byte array. I just want a simple interface for whatever first bytes I need for the information the structure displays.

If it's impossible, what's the closest thing to him?

+3


source to share


7 replies


The standard C way to overlay data types is to use 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 

      

+3


source


Apart from this, using union

(as suggested by hdante here fooobar.com/questions/2184553 / ... ) instead of trying:

const uint8_t bytes[BIGGER_THAN_STRUCT] = (struct Device) {
  .address = 123,
  .id = 456,
};

      

to do quick and dirty:

uint8_t bytes[BIGGER_THAN_STRUCT] = {0};
*((struct Device *) bytes) = ((struct Device) {
  .address = 123,
  .id = 456,
});

      



or better:

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 check the array bytes

up to the size_dev - 1

th element.

+1


source


This will be done then

static const uint8_t buffer[BIGGER_THAN_STRUCT] = {
    0x7b, 0x00, 0x00, 0x00, 
    0xc8, 0x01, 0x00, 0x00
};

      

0


source


Usually when I have to work with a bag of structured bytes, I create a "view" structure / class that gives me some higher level interface to the block of memory. Manual pointer arithmetic is usually too error-prone to repeat. Create and validate a memory-like structure.

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 where structures are initialized to access different hardware registers located in some area of ​​memory.

You can also get a pointer to a buffer, throw it into a structure, and try to use that block of memory as a structure. Possibility, but memory alignment can bite you hard. Such code may or may not work, depending on the compiler and system architecture.

0


source


I think you might want to do something like this even if you don't need a copy, since that's b_sample

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 split the structure into segments uint16_t

, you can safely replace everything uint8_t

withuint16_t

0


source


There is an alternative to using an array uint8_t byte[]

. You can also use a structure using bitfield

for each addr

and id

. You (may / may not) find it more convenient, but it provides an easy way to store the offset information associated with any given pair addr/id

.

I don't believe there is a way to directly use the struct type designated initializers to populate an array uint8_t byte

. I think the most complete initialization would be with memcpy

. I have included this in the example below. Note , nothing prevents you from filling the array uint8_t byte

memcpy

, but then you have to keep track of offset

the array uint8_t byte

to accurately point to any given byte in addr

or id

for any given element. This is where bitfield makes things a little easier. You get a one-to-one correlation between index struct Device

and index uibitfield

with a1..4

and b1..4

being bytes in each addr

and id

, respectively.

The version using the array uint8_t

is shown below this version.

Here's a quick example with test data in an array 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 unclear how you would use / fill struct Device

and how much of the initial peeking you wanted in the data in stuct Device

, so this is just intended as an example of data view.


using the uint8_t byte array:

If you want to use the array `uint8_t, the changes 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]);

      

the conclusion is the same

0


source


It:

#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? :)

0


source







All Articles