8

您好,我正在使用 C 语言进行分配,我需要将未知类型的参数传递给函数。

例如,假设我有以下内容:

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

    return 1;

}

可变元素为 void 的原因是因为有 3 种可能性。然而,所有 3 个都有一个名为“Count”的成员变量。

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

错误:请求成员“计数”不是结构或联合

我猜这是因为编译器事先不知道“元素”的类型。但是我不明白为什么这不起作用。

感谢帮助!

4

7 回答 7

12

您需要将指针转换为这 3 种类型之一,然后使用它。就像是:

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

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

于 2009-11-21T17:41:12.610 回答
10

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

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

您可以访问的唯一方法Count是将void*参数转换为正确的指针类型,然后再使用->or (*element).Count(即不仅仅是.)。

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

于 2009-11-21T17:43:22.327 回答
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);
于 2009-11-21T17:44:01.873 回答
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

于 2009-11-21T17:52:08.330 回答
1

使用演员表。

ClassName(element).count++

还有你的 element.Count = element.Count++; 行是多余的,你只需要做 element.count++(将值加一)

于 2009-11-21T17:46:41.493 回答
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、counted_double_t 或 counted_charptr_t 的指针。

发生的情况是,只要 counted_t 元素是 first,编译器就会将三个结构与“后代”结构中的 Count 元素布置在同一位置。(至少,在我用过的每一个编译器和我见过的每一段代码中。我认为这在某些时候使它成为了 C 标准,但这是一个非常正常的习惯用法。)

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

[2] x++ (postincrement) 操作递增它所应用的变量(嗯,左值);原始代码中的赋值是不必要的。

于 2009-11-21T19:02:56.487 回答
0

这正是问题所在:

我猜这是因为编译器事先不知道“元素”的类型。但是我不明白为什么这不起作用。

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

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

在 C++ 中,一种可能性是让所有三种类型都继承自同一个虚拟低音类 X,该类 X 具有方法 Count(即接口 ICountable)。然后投射到X *并使用p->Count. 这将是惯用的 C++ - 所有类都实现相同的接口,因此它们都支持这种方法。这将是一种语言支持的方法,类似于依赖于 Tommy McGuire 的答案中显示的相同结构偏移技巧,这使得结构按照惯例都相似。如果您要更改结构,或者编译器要偏离布局结构的标准,那么您将陷入困境。

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

于 2009-11-21T17:49:25.713 回答