【问题标题】:Array of struct and sizeofstruct 和 sizeof 的数组
【发布时间】:2014-11-18 06:24:52
【问题描述】:

我想用 C 语言(没有特别的风格,比如说 c11)编写一个通用的全局 const 结构数组,如下面的伪代码:

void * generic[] ={&(struct a va),&(struct b vb)};

现在我想创建一个函数,给定所需结构的位置 id(我打算为每个 id 使用一个 const,硬编码),将复制它。

因为copy()也是通用的(接受void *作为目标)它需要知道严格的大小,可以由调用者指定(很多容易出错,他已经需要提供目标结构和正确的 id) 或者我也会为每个结构维护一个正确的sizeof 的并行数组。

有没有办法在编译时自动初始化 sizeof 数组?或者甚至实现某种技巧让用户指定只是将指针传递给没有其 id 的结构,而无需编写专门的 get_struct_x()

不允许动态分配(alloc(),局部变量很好),所有结构和泛型数组内容在编译时都是已知的。

对不起,我的解释不好,请随时改进/纠正。

编辑澄清:我需要从存储了许多结构类型的数组中深度复制一个已知的结构类型,但同一类型永远不会重复。而且我需要从一个通用的 get 函数中执行此操作,女巫将从不同的线程中调用,因此在复制之前和之后将锁定一个互斥锁,并且我想将所有锁定和强制转换代码保持在一个点上,以最大限度地减少调试和创建更有效的测试。

【问题讨论】:

  • 我认为您的问题是,“我可以编写一个函数,该函数将structs 数组和索引和struct 目标地址作为输入 - 这样特定的struct 是绑定‘其他地方’?”
  • "有没有办法在编译时自动初始化 sizeof 数组?"唯一可用的机制是预处理器。它是用于抽象此类特征的最常用实用程序。
  • 是否可以选择将每个结构的大小存储在自身内部?即,添加成员 int mySize 作为每个结构中的第一个成员,并将其(在运行时)设置为 x.mySize = sizeof(struct x)
  • 或者使 void*array 成为一个结构容器数组,巫婆包含大小和 void 指针。仍然需要开发人员注意大小和结构依赖性,但容易出错并且仍然是静态初始化的。关于 struct 到 id 映射生成的任何想法?
  • 我可能完全迷路了。 什么的大小?与问题中的描述相反, non-const void *non-const 数组?数组的大小是微不足道的sizeof(generic)/sizeof(*generic) 每个实体的大小是通过void* 获得荣耀的唯一途径是不可用的。您可以拥有一个 struct { void* p; size_t len } 数组并使用实例地址和 sizeof(instance-type) 正确填充元素,但这是我看到的唯一出路。即使这样,也没有到sizeof (insert something here) 的短路径可以立即为您提供指向数据的大小。

标签: c pointers structure void-pointers


【解决方案1】:

无需修改底层数据结构,这是我能看到的唯一一种相当易于管理的方法。数组中的每个条目不是void*,而是一个包含void*(到元素)和size_t(该元素的数据大小)的结构:

#include <stdio.h>
#include <stdlib.h>

typedef struct Entry
{
    size_t len;
    void const *elem;
} Entry;

#define ELEM_ENTRY(x)   { sizeof(x), &(x) }

struct A
{
    int a1;
    long a2;
} a;

struct B
{
    float b1;
    char b2[6];
} b;

struct C
{
    unsigned int c1;
    double c2[5];
} c;

Entry generic[] =
{
    ELEM_ENTRY(a),
    ELEM_ENTRY(b),
    ELEM_ENTRY(c)
};
size_t generic_n = sizeof(generic)/sizeof(*generic);


int main()
{
    for (size_t i=0; i<generic_n; ++i)
        printf("[%zu] = %zu\n", i, generic[i].len);
}

输出

[0] = 8
[1] = 12
[2] = 44

认为这就是你想要做的。如果没有,我会放弃这个答案。请注意,如果元素是指针类型,这可能不会按您希望的方式工作(但如果它是本机 将按您希望的方式工作>array 类型)。所以要小心。

【讨论】:

  • 很抱歉删除您接受的答案,但请参阅我自己的答案,使用 X 宏做更多技巧
【解决方案2】:

我的一个朋友 [s] 没有 stackoverflow 帐户[/s] (@Francesco) 重新阐述了@WhozCraig 的答案,添加了(非常老的)X macro(感谢 Randy Meyers 的great article)构建通用结构数组和枚举女巫帮助访问数组的结构,让用户只编辑一个点;

回去工作我告诉他我想尝试制作特定的 struct getter(因为数组中不会有 REPEATED STRUCT TYPE。如果你弄错了,编译时错误,所以最后一天你会在开发时低头,但一旦编译它会更健壮),所以我们将对请求的结构的类型进行编译检查; 回到家里,他已经实施了。赞!

然后我又浪费了一些时间,让开发人员只需键入一次结构,以防止那一侧的错位; 这是优化螺旋的开始;

  • 由于专门的get和put,不再需要size数组,所以去掉了泛型结构
  • 不需要初始化结构,我们使用“匿名”结构初始化
  • 由于“匿名”初始化,直接访问结构更难
  • 直接访问需要系统知识,并具有引导意识
  • 编码更易读
  • 易于导出GENERIC_TABLE 和外部独立文件的结构定义
  • GENERIC_TABLE 上的#ifdef 工作正常,将其移至外部标题以增加可读性
  • 在局部变量之外不需要动态初始化(无垃圾,嵌入式友好)
  • 非常容易使用

成本

  • 可能是预处理器有点臃肿?
  • 似乎很多 OOP xD
  • 您可以使用未启动的指针调用 get 和 put

TL;DR:这里是完整的编译代码和基本示例:

#include <memory.h>
#include <stdio.h>
#include <stdlib.h>

/* BEGIN: just some struct to test, fell free to move them on external header file */
struct A
{
    int a1;
    long a2;
};

struct B
{
    float b1;
    char b2[6];
};

struct C
{
    unsigned int c1;
    double c2[5];
};
/* END: just some struct to test, fell free to move them on external header file */

/* this macro will create the right X macro element, and also initiliaze the "anonymous" struct */
#define ADD_STRUCT_TO_ARRAY(xu) X(xu, &(struct xu){})SEP

/* BEGIN: add or remove your own struct here! 
 * fell free to move them on external header file 
 * just need to pass struct name without "struct" to ADD_STRUCT_TO_ARRAY macro */
#define GENERIC_TABLE \
    ADD_STRUCT_TO_ARRAY(A) \
    ADD_STRUCT_TO_ARRAY(B) \
    ADD_STRUCT_TO_ARRAY(C)
/* END: add or remove your own struct here! */

/* here we initialize the enum, where the type of the struct is the key, and its position in the array the value */
#define SEP ,
#define X(a,b) a
enum STRUCT {
    GENERIC_TABLE
};
#undef X

/* here we initalize the array of pointer to the structure */
#define X(a,b) b
void * const generic[] =
{
    GENERIC_TABLE
};
#undef X
#undef SEP

/* here we create all the getter function. add here your array locking code */
#define SEP ;
#define X(a,b) void get_##a(struct a * dest){memcpy(dest, generic[a], sizeof(struct a) );} 
GENERIC_TABLE
#undef X
#undef SEP

/* here we create all the putter function. add here your array locking code */
#define SEP ;
#define X(a,b) void put_##a(struct a * source){memcpy(generic[a], source, sizeof(struct a) );}
GENERIC_TABLE
#undef X
#undef SEP

/*run, code, run!*/
int main()
{
    struct A a_copy;
    struct B b_copy;
    struct C c_copy;

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE IN ARRAY BEFORE INITIALIZATION\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    a_copy.a1 = -1;
    b_copy.b1 = 2.3;
    c_copy.c1 = 3;

    printf("STRUCTURE CHANGED TO\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    printf("STRUCTURE SAVED\n");
    put_A(&a_copy);
    put_B(&b_copy);
    put_C(&c_copy);

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE LOADED\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    a_copy.a1 = 1000;
    b_copy.b1 = -50.576;
    c_copy.c1 = 700;

    printf("STRUCTURE CHANGED TO\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);

    get_A(&a_copy);
    get_B(&b_copy);
    get_C(&c_copy);

    printf("STRUCTURE RELOADED WITHOUT SAVING\n");
    printf("A = %d\n", a_copy.a1);
    printf("B = %f\n", b_copy.b1);
    printf("C = %x\n", c_copy.c1);


    return 0;
}

【讨论】:

    【解决方案3】:

    我会将结构的大小作为每个结构中的第一个元素。所以你有

    struct a
    {
       size_t size;
       int a1;
       double a2;
       // ... 
    };
    
    struct b
    {
       size_t size;
       char b1[20];
       float b2;
       // ... 
    };
    

    任何时候你创建一个结构体,一定要初始化大小,例如

    struct a *aptr = malloc( sizeof(struct a) );
    aptr->size = sizeof(struct a);
    aptr->a1 = 3;
    aptr->a2 = 100.5;
    
    struct b someB = { sizeof(struct b), "hello", 1.0 };
    

    然后,在复制例程中,您可以将 void * 强制转换为 size_t * 以获取结构的大小,因为您知道每个结构都将大小作为第一个元素。

    【讨论】:

    • 我通常比其他两个答案更喜欢这种方法,但不幸的是,OP 明确表示该解决方案不允许使用 malloc()。所以没有+1 :-(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 2011-08-25
    • 2013-09-01
    • 2015-10-14
    • 2019-11-23
    • 1970-01-01
    • 2020-04-03
    相关资源
    最近更新 更多