有什么办法可以将结构类型传递给C函数

时间:2020-03-05 18:54:45  来源:igfitidea点击:

我有一些具有多种功能的代码,这些功能彼此非常相似,可以根据结构中一个字段的内容在列表中查找项目。函数之间的唯一区别是要在其中进行查找的结构的类型。如果可以传入类型,则可以删除所有重复的代码。

我还注意到在这些函数中也发生了一些互斥锁,所以我想我可能会把它们留在原处...

解决方案

回答

一种实现方法是将类型字段作为结构的第一个字节。接收函数查看此字节,然后根据发现的内容将指针强制转换为正确的类型。另一种方法是将类型信息作为单独的参数传递给需要它的每个函数。

回答

由于结构只不过是预定义的内存块,因此我们可以执行此操作。我们可以将void *传递给结构,并传递整数或者用于定义类型的内容。

从那里开始,最安全的方法是在访问数据之前将void *重铸为适当类型的指针。

我们需要非常非常小心,因为当我们转换为void *时会丢失类型安全性,并且在执行此类操作时可能会遇到难以调试的运行时错误。

回答

我对c有点生疏,但是尝试在函数参数中使用void *指针作为变量类型。然后将结构的地址传递给函数,然后按我们期望的方式使用它。

void foo(void* obj);

void main()
{
  struct bla obj;
  ...
  foo(&obj);
  ...
}

void foo(void* obj)
{
  printf(obj -> x, "%s")
}

回答

如果确保在每个此类结构中将字段放置在相同的位置,则可以简单地投射一个指针以获取该字段。这项技术已在许多底层系统库中使用,例如BSD插槽。

struct person {
  int index;
};

struct clown {
  int index;
  char *hat;
};

/* we're not going to define a firetruck here */
struct firetruck;

struct fireman {
  int index;
  struct firetruck *truck;
};

int getindexof(struct person *who)
{
  return who->index;
}

int main(int argc, char *argv[])
{
  struct fireman sam;
  /* somehow sam gets initialised */
  sam.index = 5;

  int index = getindexof((struct person *) &sam);
  printf("Sam's index is %d\n", index);

  return 0;
}

这样做会丢失类型安全性,但这是一种有价值的技术。

[我现在已经实际测试了上面的代码,并修复了各种较小的错误。使用编译器会容易得多。 ]

回答

我们可以使用参数化的宏来执行此操作,但是大多数编码策略对此都不满意。

#include 
#define getfield(s, name) ((s).name)

typedef struct{
  int x;
}Bob;

typedef struct{
  int y;
}Fred;

int main(int argc, char**argv){
    Bob b;
    b.x=6;

    Fred f;
    f.y=7;

    printf("%d, %d\n", getfield(b, x), getfield(f, y));
}

回答

简短的回答:不。但是,我们可以创建自己的方法,即提供有关如何创建此类结构的规范。但是,通常没有必要,也不值得为此付出努力。只是通过引用。 (callFuncWithInputThenOutput(input,&struct.output);)

回答

我认为我们应该查看C标准函数qsort()和bsearch()以获得启发。这些是用于对数组进行排序并在预排序的数组中搜索数据的通用代码。它们可以处理任何类型的数据结构,但是我们可以向它们传递指向进行比较的辅助函数的指针。辅助函数知道结构的详细信息,因此可以正确进行比较。

实际上,由于我们要进行搜索,可能只需要bsearch(),尽管如果要动态构建数据结构,则可能会决定需要与排序列表不同的结构。 (我们可以使用排序列表-与堆相比,它只会使速度变慢。但是,我们需要一个通用的heap_search()函数和一个heap_insert()函数来正确地完成工作,诸如此类在Web上搜索功能会发现该功能不存在该名称;请不要尝试" c堆搜索",因为假定意思是"便宜的搜索",并且会收到大量垃圾!)

回答

如果我们测试的ID字段是所有结构共享的通用字段初始序列的一部分,那么使用并集保证访问将起作用:

#include <stdio.h>

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int id;
    long junk2;
} Bar;

typedef union
{
    struct
    {
        int id;
    } common;

    Foo foo;
    Bar bar;
} U;

int matches(const U *candidate, int wanted)
{
    return candidate->common.id == wanted;
}

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 42, 0 };

    U fu;
    U bu;

    fu.foo = f;
    bu.bar = b;

    puts(matches(&fu, 23) ? "true" : "false");
    puts(matches(&bu, 42) ? "true" : "false");

    return 0;
}

如果我们不走运,并且该字段在各种结构中以不同的偏移量出现,则可以向函数添加偏移量参数。然后,offsetof和包装器宏模拟OP要求在调用站点传递结构类型的内容:

#include <stddef.h>
#include <stdio.h>

typedef struct
{
    int id;
    int junk1;
} Foo;

typedef struct
{
    int junk2;
    int id;
} Bar;

int matches(const void* candidate, size_t idOffset, int wanted)
{
    return *(int*)((const unsigned char*)candidate + idOffset) == wanted;
}

#define MATCHES(type, candidate, wanted) matches(candidate, offsetof(type, id), wanted)

int main(void)
{
    Foo f = { 23, 0 };
    Bar b = { 0, 42 };
    puts(MATCHES(Foo, &f, 23) ? "true" : "false");
    puts(MATCHES(Bar, &b, 42) ? "true" : "false");

    return 0;
}