【问题标题】:Dynamically access member variable of a structure动态访问结构的成员变量
【发布时间】:2014-04-29 06:47:53
【问题描述】:

我有不同的结构,我想编写一个通用框架来从 C 中的哈希表或这些结构的列表中获取元素的排序列表。例如,如果我有一个结构定义为

struct XYZ{ 
 int a;
 char b[20];
 long time;
}

假设我有一个这种结构类型的元素列表。我需要一个功能,通过它我可以按结构的任何字段对该列表进行排序,并且该字段将由用户给出。这种用例也必须可用于其他此类结构,因此这种排序框架应该是通用的。我正在考虑使用某些脚本以不同的自动生成结构的形式存储与此结构相关的不同元数据。该元数据将存储与给定结构中每个元素相关的信息。此信息将包括结构的类型(即整数、字符串或日期:执行排序操作时不同的类型)以及访问结构中此元素的方法。现在我的问题是访问这些元素的理想方式应该是什么。一种直接的方法是为这些中的每一个生成一些 getter 函数,并在自动生成的元数据结构中设置 getter 函数的函数指针。但是拥有一个访问每个成员元素的 getter 函数将意味着每个元素访问的函数调用。这会阻止应用程序的性能吗?有没有其他方法可以做到这一点?也欢迎任何其他实现所需用例的框架想法。

【问题讨论】:

  • 一般来说这是不可能的。解决它的正常方法是有一个排序函数(建议使用标准的qsort 函数)和一组比较器函数,结构中的每个成员一个函数。
  • 那么这是否意味着我对每个结构的每个元素都有比较函数?
  • 出于好奇:char *b[20] 是 20 个字符 指针,而不是最多 20 个字符的字符串。如果这是您的意图,那么您必须为结构的每个成员编写一个单独的 qsort 回调,如果不是,您可以使用 offsetof + 全局变量 + 通用回调
  • 我的错。更正了它。谢谢!

标签: c pointers data-structures function-pointers


【解决方案1】:

正如 cmets 所建议的,最直接的方法是使用 qsort 并为结构的每个成员调用一个回调。
可以使用offsetof 宏来创建更通用的单一回调。

单个回调的危险在于,每当结构更改时,您都必须更改函数。它还需要您创建一个全局变量,这也不理想。当然,如果你使用单独的回调,你也必须创建一个新函数。但是,您没有全局变量,而且忘记创建新函数更容易调试和修复。

但作为一个例子,这里有一个通用的qsort回调的可能方法:

#include <stddef.h>

static size_t member_offset;

int struct_cmp(const void *a, const void *b)
{
    switch(member_offset)
    {
         case 0://or case offsetof(struct XYZ, a):
             return ((struct XYZ *)a)->a - ((struct XYZ *) b)->a;
         case offsetof(struct XYZ, b):
             return strcmp(
                 ((struct XYZ *)a)->b,
                 ((struct XYZ *)b)->b
             );
         case offsetof(struct XYZ, c):
             return ((struct XYZ *)a)->c - ((struct XYZ *) b)->c;
         default:
             fprintf(stderr, "Invalid offset value %d\n", member_offset);
             exit( EXIT_FAILURE );//possible bug
    }
}

int main ( void )
{
    struct XYZ foo[10];//create array of structs
    size_t offsets[3] = { 0, offsetof(struct XYZ, b), offsetof(struct XYZ, c)};
    for (int i=0;i<3;++i)
    {
        member_offset = offsets[i];//omit this, and you're in trouble!
        qsort(
            foo,
            sizeof foo,
            sizeof *foo,
            struct_cmp
        );
    }
    return 0;
}

正如您已经看到的,如果结构体有 10 个成员字段,那么回调将看起来像一团糟。由于使用全局变量,它也必然更容易受到攻击。未初始化全局变量,或在另一个线程排序时更改全局变量的状态...
老实说:只需为每个成员添加一个函数,但也许创建一个函数来保存 qsort 调用:

void sort_structs(struct XYZ *structs[], size_t count, size_t member_offset)
{//Pass POINTER to array
    switch (member_offset)
    {
        case 0://same cases as above:
            qsort(
                *structs,
                count,
                sizeof *structs[0],//or **structs
                a_sort_callback
            );
            break;
        case offsetof( struct XYZ, b):
            qsort(
                *structs,
                count,
                sizeof *structs[0],
                b_sort_callback
            );
            break;
        case offsetof( struct XYZ, c):
            qsort(
                *structs,
                count,
                sizeof *structs[0],
                c_sort_callback
            );
            break;
        default:
            fprintf(
                stderr,
                "Either %d is invalid offset, or you haven't implemented a callback"
                member_offset
            );
            exit( EXIT_FAILURE );
    }
}

【讨论】:

  • 谢谢,这种方法看起来我可以利用。很可能我不会使用 inbuild qsort 函数,而是使用我们自己编写的东西。所以我可以避免使用全局变量,并在需要时以某种方式计算偏移量。
  • @labyrinth:计算偏移量时不需要“somehow”offsetof 宏是标准的,可以在stddef.h 中找到。结构的第一个成员的偏移量始终为0,因此也许您可以将该成员设置为指向您要排序的成员,但这只是一个非常混乱的做事方式:)
【解决方案2】:

作为Joachim Pileborg pointed out,在C 标准库中有一个名为qsort() 的函数。您可以将其完全用于您的目的。但是你必须知道你有什么类型的数组,并为它们使用适当的排序函数。

如果出于任何原因,您需要对不同的结构类型进行排序(如果您通过指针进行排序,这将起作用),它们中的每一个都应该提供一个键函数来提取您想要排序的值。

如果这样做,您就可以有效地用 C 语言构建一个 OOP 框架。

但更好的方法是拥有一个统一的数组,并为您想要排序的每种类型提供一个排序函数。

【讨论】:

  • “你必须通过一个公共字段来扩展你的结构”——在不属于它们的地方引入 OO 概念是一种极其错误和注定的尝试。不要那样做孩子!
  • @n.m 如果您想将一些结构组件排序为字符串,将其他结构组件排序为 int,而将其他结构组件再次排序为 double,会怎么做?
  • 我会使用qsort 或完全按照它的设计用途,传递不同的比较器函数not 存储在我的结构中。我为什么要把它们存放在那里?我只需要一个比较器函数来对数组进行排序,而不是每个元素一个比较器函数。
  • @n.m.你是对的——对于排序,数组必须是统一的——除非你选择我指出的关键函数方法。但这只有在至少类型相同的情况下才有意义。我会编辑...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-17
  • 1970-01-01
  • 1970-01-01
  • 2014-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多