C:在编译时初始化结构中的自定义数组大小

30byixjq  于 2022-12-03  发布在  其他
关注(0)|答案(6)|浏览(187)

请考虑以下代码:

typedef struct Object
{
  uint8_t size;
  uint8_t array[];
}Object;

我如何使用编译时但用户定义大小的数组创建对象?
在阅读了一些stackoverflow的帖子并亲自尝试了代码之后,我现在知道只要我至少使用编译器标准C99,我就可以使用如下自定义大小声明Object:

Object o = {.size = 2, .array = {1, 2}};

但我想要的只是一个空数组,它有自定义的大小,所以不用输入

Object o = {.size = 5, .array = {1, 2, 3, 4, 5}};

我想键入类似于

Object o = {.size = 5, .array = uint8_t[5]};

同样重要的是,我需要在编译时知道数组的大小,并且不能使用动态内存(如malloc),因此仅使用malloc分配的灵活数组成员的典型解决方案对我不起作用。
是否存在这样的解决方案?
如果不存在:为什么第一个例子可以工作(对于某些编译器),但只是告诉编译器我在编译时需要结构体中的一个大小为10的数组,而不初始化其中的值就不行呢?
编辑:我想我应该清楚地表明,我的目标是创建类似于C++ std::array的数据类型,因为使用该数据类型,用户只需选择数组的大小,而不必了解内部工作原理,也不必为每个新的大小重写结构体。

x9ybnkn6

x9ybnkn61#

您可以使用在复合常值内定义的并集:

Object *o = &(union { Object o;
                      // anonymous struct
                      struct { uint8_t size; uint8_t array[N]; };
                    } ) { .size = N }.o;

它可以很好地打包到宏中。
它在pedantic模式下编译时不会发出警告,为全局对象和堆栈分配的对象生成预期结果。请参见https://godbolt.org/z/jhsbMseY8
这个解决方案可以保证有效,因为在C语言中,联合的两个结构体成员可以使用别名,只要它们共享共同的初始子序列。

cvxl0en2

cvxl0en22#

我建议您为所需的每个Object大小定义一个类型。
您可以通过定义一些宏来简化它:

#include <stdint.h>
#include <stdlib.h>

#define OBJINIT(x) (Object##x){.size = x }
#define DEFOBJ(x)     \
typedef struct        \
{                     \
  uint8_t size;       \
  uint8_t array[x];   \
} Object##x;          \
Object##x *CreateObject##x() {          \
    Object##x *rv = malloc(sizeof *rv); \
    if(!rv) exit(1);                    \
    *rv = OBJINIT(x);                   \
    return rv;                          \
}

// Define the types with the sizes you need:
DEFOBJ(2)
DEFOBJ(5)

int main(void) {
    Object2 o2 = OBJINIT(2);         // an initialized automatic variable
    
    Object5* o5p = CreateObject5();  // an initialized dynamically allocated variable
    free(o5p);
}
vwhgwdsa

vwhgwdsa3#

像这样分配数组大小以便您可以轻松地更改它并在代码中的其他位置使用它的大小的常规方法是使用宏。

#define MYARRAYSIZE 10

typedef struct 
{
  uint8_t size;
  uint8_t array[MYARRAYSIZE];
}Object_t;

另一种方法是把它变成一个指针,然后在运行时用malloc动态地分配内存。

#define MYARRAYSIZE 10

typedef struct 
{
  uint8_t size;
  uint8_t *array;
}Object_t;
 ...
Object_t Object;
Object.array = (uint8_t *)malloc(MYARRAYSIZE*sizeof(uint8_t));
2vuwiymt

2vuwiymt4#

如果一个人使用方言,通过充分放松“严格别名规则”来扩展语言的语义,以维护C语言的精神原则“不要阻止程序员做需要做的事情”,一个常见的方法是这样做:

struct polygon { int sides; int coords[]; };

struct poly5 { int sides; int coords[10]; }
  const myPentagonn = {5, {0,-5, 4,-3, 3,5, -3,5, -4,-3} };

#define myPentagon (*((struct polygon*)&myPentagonn))

在标准之前的方言中,通常需要将多边形声明为包含零元素数组,或者如果编译器决定让人讨厌,则声明为单元素数组,但是对于函数是否应该能够像这里所示例的那样可互换地作用于多个类似的结构类型,从来没有任何疑问。这种结构,经过一些调整,在1974年的C语言方言中得到了认可。并且适合于低级编程的方言将支持它们,而不管该标准是否要求这种支持。
一些clang/gcc爱好者不接受标准作者在已发布的基本原理文档中所表达的意图,他们坚持认为任何需要使用-fno-strict-aliasing的代码都将被破坏。将对这些结构的支持作为标准权限之外的实现质量问题来处理。旨在用于将永远不需要任何种类的低级存储器操纵也不从任何种类的低级存储器操纵中受益的任务的实现可能比适应这种需要的实现 * 对于这种任务 * 更有效,但这并不意味着打算在较低级别使用内存的代码应该跳过障碍以与此类专用实现或配置兼容。

tnkciper

tnkciper5#

#include <stdio.h>
#include <stdint.h>

typedef struct Object
{
  uint8_t size;
  uint8_t array[5];
} Object;

int main() {
    Object o = { .size = 2,  {1, 2}};
    printf("%d %d\n", o.array[0], o.array[1]);
}

在macOS上用cc -std=c99在clang 14.0.0中编译。它输出:

ovfsdjhp

ovfsdjhp6#

首先,每当你发现自己有这样的需求时,请退一步,看看是否能简化程序。使用晦涩的宏黑客永远是最后的手段--即使这样,大多数情况下也只是在维护旧代码时。
话虽如此,最简单的解决方案可能是根据变量名为每个对象创建一个唯一的typedef,然后在同一个宏中声明一个该类型的变量,这样就可以在任何地方使用它。
一个可能的解决方案是:

#define create_obj(name, ...)                        \
typedef struct                                       \
{                                                    \
  uint8_t size;                                      \
  uint8_t array[sizeof( (uint8_t[]){__VA_ARGS__} )]; \
} obj_##name;                                        \
                                                     \
obj_##name name =                                    \
{                                                    \
  .size = sizeof( (uint8_t[]){__VA_ARGS__} ),        \
  .array = { __VA_ARGS__ }                           \
}

将创建追加了变量名的类型。它将不使用灵活的数组成员,而是使用固定大小的数组。它将不需要动态分配,也不需要依赖特殊的极端情况规则或非标准扩展。
如果要使用uint8_t以外的其他成员类型,则只需使用通常的技巧来确定数组大小:
sizeof((type[]){ __VA_ARGS__ }) / sizeof(type[0])
示例:

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

#define create_obj(name, ...)                        \
typedef struct                                       \
{                                                    \
  uint8_t size;                                      \
  uint8_t array[sizeof( (uint8_t[]){__VA_ARGS__} )]; \
} obj_##name;                                        \
                                                     \
obj_##name name =                                    \
{                                                    \
  .size = sizeof( (uint8_t[]){__VA_ARGS__} ),        \
  .array = { __VA_ARGS__ }                           \
}

create_obj(filescope_obj,1,2,3);

int main (void) 
{
  create_obj(local_obj,1,2,3,4,5);

  for(size_t i=0; i<filescope_obj.size; i++)
  {
    printf("%"PRIu8 " ", filescope_obj.array[i]);
  }
  puts("");
  for(size_t i=0; i<local_obj.size; i++)
  {
    printf("%"PRIu8 " ", local_obj.array[i]);
  }
}

输出量:

相关问题