4

我想要一个接受不同类型结构作为参数的函数。所以,由于我没有特定的类型,我必须使用 void*。现在的问题是:当我将结构传递给这个函数时,我怎样才能在函数内部访问这个结构的已知成员?具体来说,我知道所有结构都有 str1 作为成员,例如,我想要打印它。这是一个示例代码:

struct {  
  char* str1;  
  float tt1; } var1 = {"This is me", 12};  

struct {  
   char* str1;  
   int   tt2; } var2 = {"This is you", 18};  

void printStruct(void* str)  
{  
   printf("\n the structure string is %s", ??);   
  //can I put something in ?? to print the string?  
}

main(....)  
{  
   printStruct(&var1);  
   printStruct(&var2);  
}
4

6 回答 6

7

您可以传递函数指针以将函数与其用户分离

void printStruct(void* data, char * (*namef)(void *data)) {  
   printf("\n the structure string is %s", namef(data));    
}

然后假设你将 a 传递struct S1*给函数,你可以像这样传递一个 getter:

char * whois(void *data) {
  return ((struct S1*)data)->str1;
}

int main(void) {
  struct S1 s = {"This is me", 12};
  printStruct(&s, whois);
}

你可以使用更丑陋的替代offsetof方案

void printStruct(void* data, size_t named) {  
   printf("\n the structure string is %s", *(char**) ((char*)data + named));    
}

int main(void) {
  struct S1 s = {"This is me", 12};
  printStruct(&s, offsetof(struct S1, str1));
}

offsetof是一个宏,它为您提供结构内成员的偏移量。在函数中,您将指针定位到char*成员(因此您必须强制转换char**以获得适当的类型)并获取成员值。

但是我更喜欢第一种使用函数的方式。这样,打印函数甚至不必知道data指向结构。它可以将它作为黑盒交给 getter 函数,在其中您可以让编译器为您处理低级偏移计算。

于 2010-04-09T20:23:33.200 回答
6

您可以将其转换为您想要的类型,然后访问该成员,例如,假设您有原型,请执行以下操作:

struct X
{
char* str1;  
float tt1; } var1 = {"This is me", 12};  

void printStruct(void* str)
{
    ((struct X *)str)->str1;
    ...
}

注意括号的使用;发生的情况是将 void* 类型转换为 struct var2 * ,并且该类型在最外面的 () 括号内可以作为该类型的变量进行访问。

于 2010-04-09T20:15:14.587 回答
4

你应该给你的结构命名。例如,

struct foo {
   char * str1;
   float tt1;
};

然后你可以像这样声明你的实例:

struct foo var1 = { "This is me", 12 };

然后你可以在你的函数中强制void *转换为 a :struct foo *

void printStruct(void * ptr) {
   printf("str=%s\n", ((struct foo *)ptr)->str1);
}

你可以这样称呼它:

int main(...) {
   struct foo var1 = { "This is me", 12 };
   printStruct(&var1);
}

更新:评论者约翰内斯是正确的,我的回答并没有说明原发帖人说他可能不知道每个结构的类型。更新的解决方案如下:

由于您知道所有结构都包含 a char * str1,因此您可以通过将结构直接转换为仅包含 的通用标记结构来利用 C 的“平面内存模型” char * str1这仅适用于 structschar * str1第一个元素。

struct base { char * str1; };
struct { char * str1; float tt1; } var1 = { "This is me", 12 };
struct { char * str1, int   tt2; } var2 = { "This is me", 18 };

void printStruct(void * ptr) {
   printf("str is %s\n", ((struct base *)ptr)->str1);
}

不过,这是一个非常肮脏的黑客攻击,IMO。如果您要共享其他成员,您将无法使用相同的技巧。

另一种选择是定义一个struct使用带有枚举数的联合来跟踪联合中实际使用的类型:

enum { FLOAT_TYPE, INT_TYPE };
struct foo {
   char * str1;
   union {
      float tt1;
      int tt2;
   } numericValue;
   int unionType;
};

这将允许您定义如下变量:

struct foo var1 = { .str1 = "This is me", .numericValue.tt1 =12, .unionType = FLOAT_TYPE };
struct foo var2 = { .str1 = "This is me", .numericValue.tt2 =18, .unionType = INT_TYPE };

然后你不需要处理不同类型的结构,你可以将你的 void 指针转换为struct foo *

void printStruct(void * ptr) {
   struct foo * p = (struct foo *)ptr;
   printf("string is %s\n", p->str1);
}

这可能需要更多的工作,但它更清洁,IMO。

于 2010-04-09T20:20:37.600 回答
2

对于大多数编译器来说,以这种方式强制转换和访问成员是安全的,因为它是两个结构的第一个成员,对齐方式很可能是相同的。

事情是这样的,因为您需要将指针转换为它们需要有标签的结构:

struct S1 { char* str1; float tt1; } var1 = {"This is me", 12};

struct S2 { char* str1; int tt2; } var2 = {"This is you", 18};

void printStruct(void* str) { printf("\n the structure string is %s", ((struct S1 *)str)->str1); }

但是请注意,对于具有更多变量和不同顺序的更复杂示例,这可能不安全,这完全取决于编译器选择的对齐方式。

于 2010-04-09T20:17:57.313 回答
2

首先,一个简单的例子:

int booger(int type, void * st) {
   switch(type) {
      case foo:
        struct foo * f = (struct foo *)st;
        /// do stuff
        break;
      case bar:
      /// ...

现在,对于您的结构:定义一个仅包含第一个成员的新结构 - 字符串: struct st { char * str; }; 现在您可以通过将所有其他结构转换为 virtual_st * 将它们传递到您的函数中,然后执行与第一个示例中相同的操作来获取实际成员。

int baz(struct st * s) {
    if (!strcmp(s->str, "foo") ) {
       // ...

编辑: 顺便说一句,这类似于输出 c 的 c++ 编译器的操作方式,除了初始字段是指向类型结构的指针,该类型结构包含成员函数和静态类成员的函数指针。不过,我不知道多重继承是如何工作的。

于 2010-04-09T20:18:49.713 回答
0

如果您传入 avoid*我不知道该方法如何知道您传入的内容,除了它是某个东西的地址。这意味着您必须先转换它才能使用它,否则请确保struct您传入的所有 schar*每次都在完全相同的位置。

于 2010-04-09T20:17:45.570 回答