在C中传递Void类型参数

时间:2009-11-21 17:37:21

标签: c void-pointers

你好,我正在C中进行一项赋值,我需要将一个未知类型的参数传递给一个函数。

例如假设我有以下内容:

int changeCount(void* element)
{
    element.Count = element.Count++;

    return 1;

}

变量元素无效的原因是因为有三种可能性。但是,所有3都有一个名为“Count”的成员变量。

当我尝试编译我在Eclipese中编写的实际代码时,我收到以下错误:

  

错误:请求成员'计数'   不是结构或联合的东西

我猜这种情况正在发生,因为编译器事先并不知道“元素”的类型。但是,我不明白为什么这不起作用。

感谢您的帮助!

7 个答案:

答案 0 :(得分:12)

您需要将指针强制转换为这3种类型中的一种,然后再使用它。类似的东西:

MyType* p = (MyType*)element;
p->count++;

但是,您需要确定要投射的对象的类型,因为将对象强制转换为错误的类型可能会很危险。

答案 1 :(得分:9)

首先,您传入一个指向void的指针,这是一种针对未知类型的有效方法,但您需要将指针传递给不同的对象类型。

C不是动态语言,因此符号类型信息在运行时被大量删除,所以当你说你的三种类型都有一个成员Count时,这对你的功能设计没有帮助。

您可以访问Count的唯一方法是在使用void*->进行解除之前将(*element).Count参数转换为正确的指针类型(即不仅仅是{{1} }})。

除非您依赖于具有兼容布局的类型(可能依赖于实现),否则您还需要传递一些有助于您的函数确定要执行的正确转换的内容。在这一点上,你可能会有更好的三个独立功能和更好的类型安全性。

答案 2 :(得分:6)

您必须将其强制转换为实际类型,但结果中的结果可能会在同一位置没有count属性。

typedef struct _A 
{
    int count;
    int x;
}A;
int changeCount(void* element)
{
    ((A*)element)->Count++;

    return 1;

}

还要记住,如果将指针传递给类似于以下内容的结构,则会增加x字段而不是计数。

typedef struct _B 
{
    int x;      
    int count;

}B;

    B st;
    B* pst = &st;
    changeCount(pst);

答案 3 :(得分:3)

如果.Count元素对于这3种类型中的每一种都属于同一类型,那么您也可以使用宏。假设它是int,你可以这样做:

#define changeCount(a) _changeCount((a), &(a)->Count)

int _changeCount(void* element, int *count)
{
  (*count)++;
  return 1;
}

这将有效,因为a.Count地址将在您调用函数时解析,而不是在(当您不再知道该类型时)之后解析。我假设你在调用函数时有正确的类型。因此,Sometype x; changeCount(x);会起作用,但传入已经是(void*)的内容则不会。

你原来的表达方式element.Count = element.Count++;也很奇怪。如果您想增加,请使用element.Count++element.Count = element.Count + 1

答案 4 :(得分:1)

使用演员。

ClassName(element).count++

也是你的element.Count = element.Count ++; line是多余的,你只需要做element.count ++(将值递增1)

答案 5 :(得分:1)

编译时,C丢弃[1]大多数类型信息,只留下偏移量。所以,你的函数会在伪代码中编译成这样的东西:


changeCount:
  assign *(*(stack_ptr + offset_element) + offset_Count) + 1
    to *(stack_ptr + offset_element) + offset_Count;
  assign 1 to return_value;
  pop

stack_ptr是调用changeCount时创建的堆栈帧的位置,offset_element是元素参数相对于stack_ptr的位置,但是offset_Count是什么?请记住,所有编译器都知道您的代码就是您在示例中显示的内容; element是一个通用指针,实际上并不是指向任何东西的指针。您必须通过转换或将其赋值给变量[2]来告诉编译器指向哪个元素:


typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
    counted_t* counted = element;
    counted.Count++;
    return 1;
}

此函数将生成与上面基本相同的(伪)代码,但编译器现在知道Count的偏移应该是什么。

您提到元素指向的类型有三种可能性。有两种方法可以处理:区分联合或“继承”结构。例如,对于一个杰出的联合使用,一个元素是一个枚举的结构,用于识别三种可能性中的哪一种,另一种元素是三种可能结构的联合;这大致是ML语言(OCaml,Haskell等)称为代数数据类型或者是Pascal中的联合。对于“继承”,您可以使用类型定义:


typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;

在这种情况下,您可以使用上面的changeCount函数并传入指向counted_int_t,counting_double_t或counting_charptr_t的指针。

只要count_t元素是第一个,编译器将在同一位置的“后代”结构中布置具有Count元素的三个结构。 (至少,在我曾经使用过的每一个编译器中以及我见过的每一段代码中。我认为这在某种程度上使它成为C标准,但这是一个非常正常的习惯用法。)

[1]除了调试信息之外,如果你告诉编译器发出它。但是,您的程序无法访问该信息,因此在这种情况下无效。

[2] x ++(postincrement)操作增加了它应用的变量(well,lvalue);原始代码中的分配是不必要的。

答案 6 :(得分:0)

这正是问题所在:

  

我猜这种情况正在发生   因为编译器不知道   手前的“元素”类型。然而   我不明白为什么这不起作用。

按名称调用方法或成员数据通常不是静态类型语言的功能。

即使void *只能被可靠地转换为T *,其中T是一种类型,当指针实际上是指向该类型的指针时。

在C ++中,一种可能性是让所有三种类型都从相同的虚拟低音类X继承,其具有方法Count(即接口ICountable)。然后转换为X *并使用p->Count。这将是惯用的C ++ - 所有类都实现相同的接口,因此它们都支持这种方法。这将是一种语言支持的方法,类似于依赖于Tommy McGuire的答案中显示的相同结构偏移技巧,这使得结构类似按惯例。如果你要改变结构,或者编译器偏离了布置结构的标准,那么你将处于热水中。

我不禁认为这是一个玩具问题,因为这个方法非常简单,人们通常不会将其包装在函数中 - 人们只需将其称为内联:T t; t.Count++;