使用结构体初始化C数组

3

是否可以使用结构体初始化一个uint8_t数组?

我想要实现类似以下的效果:

#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,
};

我想要这样做的原因是为了方便查看我写入字节数组的内容。我只是想要一个简单的接口来获取结构体显示的信息所需的前几个字节。

如果不可能,有什么最接近它的东西吗?


4
第一件事,你为什么想要这样做? - Sourav Ghosh
所以你基本上只是想把结构体的值放入数组中? - Etheryte
1
我更新了帖子并附上了原因。我正在使用嵌入式系统,在其中保留了更大的一块内存,但希望以简单的方式初始化用于该结构的部分。 - Niklas Norin
1
无论原因是什么,你都不能让 uint8_t 持有值为 456 的数。 - Sourav Ghosh
不,但这也不是我想要做的。我试图使用结构体初始化程序来初始化数组。如果这样能行的话,那么uint32_t将会在4个uint8_t上布置。 - Niklas Norin
这个问题很有趣,你选择哪个答案?还是不选? - Iharob Al Asimi
7个回答

3

C语言中覆盖数据类型的标准方式是使用联合体:

    #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 

1

除了尝试使用union(如此处hdante所建议 https://dev59.com/DIbca4cB1Zd3GeqPQgfA#27462808),而不是尝试:

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

做快速且不完美的:

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

或者更好的做法是:
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);

然后检查数组bytes直到第size_dev - 1个元素。


0

那就这样做吧

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

如果你想测试它,请执行 memcpy(&device, buffer, sizeof(struct Device)); 并打印结果以验证其正确性。 - Iharob Al Asimi

0
通常,当我需要处理结构化字节包时,我会创建一个“视图”结构/类,以便为我提供一些更高级别的接口来访问内存块。手动指针算术通常太容易出错而不会重复。创建一个“内存视图”结构并进行适当的单元测试。
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;

在设备驱动程序中,您可以看到类似的技术,当结构被初始化以访问位于某个内存区域中的不同硬件寄存器时。

您还可以获得缓冲区指针,将其转换为结构,并尝试使用此内存块作为结构。虽然可行,但内存对齐可能会给您带来麻烦。这样的代码可能会或可能不会工作,具体取决于编译器和系统架构。


你声明了 void view_init(struct memory_view *view, void *buf, size_t bufsz),但是调用的时候却写成了 view_init(&view, buffer)。你想要实现什么功能? - David C. Rankin
这只是个拼写错误,没有别的问题。 - ezaquarii

0

我认为你可能想做类似的事情,即使复制并不是那么必要,因为b_sample正是你所需要的。

#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;
}

演示:http://codepad.org/wE8dbBV1 如果您想将结构体分成uint16_t段,可以安全地将所有uint8_t替换为uint16_t

0

使用 uint8_t byte[] 数组的替代方案是使用结构体,利用每个 addridbitfield。你可能会发现它更方便,但它确实提供了一种简单的方法来保持与任何给定的 addr/id 对关联的偏移信息。

我不相信有一种直接利用结构体类型指定初始化器来填充uint8_t byte数组的方法。我认为最接近完整初始化的方法是使用memcpy。我在下面的示例中包含了它。请注意,没有任何东西阻止您使用memcpy填充uint8_t byte数组,但是您必须跟踪uint8_t byte数组中的offset,以准确地指向addrid中的任何给定字节。这就是位域使事情变得稍微容易的地方。您可以在a1..4b1..4中找到每个addrid中的字节。

下面是一个使用uint8_t数组的版本。

这里是一个简短的示例,其中包含一个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;
}

输出:

$ ./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)

注意:不清楚您将如何使用/填充“struct Device”以及您希望在“stuct Device”中查看多少初始数据,因此这只是一个查看数据的示例。

使用uint8_t字节数组:

如果您确实想要使用`uint8_t`数组,则需要进行的更改很少:

    /* 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]);

输出相同


0

这个做什么:

#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;

行得通吗? :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接