【问题标题】:Initialize C array with struct用结构初始化 C 数组
【发布时间】:2014-12-13 13:30:38
【问题描述】:

是否可以用 struct 初始化一个 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,
};

我之所以这样做是为了获得我写入字节数组的内容的简单叠加视图。我只是想要一个简单的接口来连接结构显示的信息所需的任何第一个字节。

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

【问题讨论】:

  • 第一件事。你为什么要这么做?
  • 所以你本质上只是想将结构值放入一个数组中?
  • 我更新了帖子的原因。我正在使用一个嵌入式系统,我在其中保留了更大的内存,但想以一种简单的方式初始化用于结构的部分。
  • 不管是什么原因,456 的值不能被uint8_t 持有
  • 不,但这也不是我想要做的。我正在尝试使用结构初始化程序初始化数组。如果这可行,那么 uint32_t 将布置在 4 个 uint8_t 上。

标签: c arrays initialization


【解决方案1】:

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 

【讨论】:

    【解决方案2】:

    除了通过union (由hdante 在这里https://stackoverflow.com/a/27462808/694576 提出)来解决这个问题,而不是尝试:

    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 - 1th 元素。

    【讨论】:

      【解决方案3】:

      这样就可以了

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

      【讨论】:

      • 如果您想测试它,请执行memcpy(&amp;device, buffer, sizeof(struct Device)); 并打印值以查看它们是否正确。
      【解决方案4】:

      我认为您可能想做类似的事情,即使副本不是那么必要,因为 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

      【讨论】:

        【解决方案5】:

        通常,当我必须使用结构化字节包时,我会创建一个“视图”结构/类,它为我提供了一些到内存块的更高级别的接口。手动指针运算通常太容易出错而无法重复。创建一个“内存视图”结构并对其进行适当的单元测试。

        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(&amp;view, buffer)。你想完成什么?
        • 打错字了。仅此而已。
        【解决方案6】:

        这样做:

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

        成功了吗? :)

        【讨论】:

          【解决方案7】:

          除了使用uint8_t byte[] 数组之外,还有另一种方法。您还可以使用一个结构,该结构为每个addrid 使用bitfield。您(可能/可能不会)发现它更方便,但它确实提供了一种简单的方法来保持与任何给定 addr/id 对关联的偏移信息。

          我不相信有办法直接使用结构类型 Designated Initializers 来填充uint8_t byte 数组。我认为最接近的完全初始化是memcpy。我已将其包含在下面的示例中。 注意,没有什么可以阻止您使用memcpy 填充uint8_t byte 数组,但是您必须跟踪uint8_t byte 数组中的offset 以准确指向任何给定字节在任何给定元素的addrid 中。这是位域使事情变得更容易的地方。您会在struct Device 索引和uibitfield 索引之间获得一对一 相关性,其中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]);
          

          输出相同

          【讨论】:

            猜你喜欢
            • 2011-05-09
            • 1970-01-01
            • 1970-01-01
            • 2014-02-26
            • 2010-09-23
            • 2010-12-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多