在C中传递Void类型参数
你好,我正在 C 中进行一项作业,我需要将未知类型的参数传递到函数中。
例如假设我有以下内容:
int changeCount(void* element)
{
element.Count = element.Count++;
return 1;
}
变量元素为空的原因是因为有 3 种可能性。然而,所有 3 个都有一个名为“Count”的成员变量。
当我尝试编译我在 Eclipese 中编写的实际代码时,出现以下错误:
错误:请求成员“Count” 不是结构或联合的东西
我猜发生这种情况是因为编译器事先不知道“元素”的类型。但是我不明白为什么这不起作用。
感谢您的帮助!
Hello there I am working on an assignment in C where I need to pass in an unknown type of parameter into a function.
For example suppose I have the following:
int changeCount(void* element)
{
element.Count = element.Count++;
return 1;
}
The reason why variable element is void is because there are 3 types of possibilities. All 3 however do have a member variable named "Count".
When I try to compile the actual code I wrote in Eclipese, I get the following error:
error: request for member ‘Count’ in
something not a structure or union
I am guessing this is happening because the compiler doesn't know the type of "element" before hand. However I don't see why this isn't working.
Thanks for help!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
编译时,C 会丢弃[1]大部分类型信息,只留下偏移量。因此,您的函数将在伪代码中编译为如下所示:
stack_ptr 是调用 ChangeCount 时创建的堆栈帧的位置,offset_element 是元素参数的位置,相对于 stack_ptr,但 offset_Count 是什么?请记住,编译器对您的代码的所有了解都只是您在示例中显示的内容; element 是一个通用指针,而不是真正指向任何东西的指针。您必须通过强制转换或将其分配给变量来告诉编译器指向哪个元素[2]:
此函数将生成与上面基本相同的(伪)代码,但编译器现在知道 Count 的偏移量应该是多少是。
您提到元素指向的类型有三种可能性。有几种处理方法:要么是杰出的联合,要么是“继承”结构。对于区分联合使用,例如,一个结构,其中一个元素是枚举,标识三种可能性中的哪一种,另一个元素是三种可能结构的联合;这大致就是 ML 语言(OCaml、Haskell 等)所称的代数数据类型或 Pascal 中的并集。对于“继承”,您可以使用类型定义:
在这种情况下,您可以使用上面的changeCount函数并传入指向counted_int_t、counted_double_t或counted_charptr_t的指针。
发生的情况是,只要 counted_t 元素位于第一个,编译器就会将“后代”结构中带有 Count 元素的三个结构布置在同一位置。 (至少,在我用过的每一个编译器和我见过的每一个代码中。我认为这在某种程度上成为了 C 标准,但这是一个非常正常的习惯用法。)
[1] 除了调试信息,如果您已告诉编译器发出它。不过,您的程序将无法访问该信息,因此在这种情况下它没有帮助。
[2] x++(后递增)操作递增它所应用到的变量(好吧,左值);原始代码中的分配是不必要的。
When compiling, C discards[1] most type information, leaving only offsets. So, your function would compile to something like this, in pseudocode:
The stack_ptr is the location of the stack frame that was created when you call changeCount, the offset_element is the location of the element argument, relative to the stack_ptr, but what is offset_Count? Keep in mind, all the compiler knows about your code is just what you have shown in your example; element is a generic pointer, not really a pointer to anything. You will have to tell the compiler what element is pointing to, by casting or assigning it to a variable[2]:
This function will generate essentially the same (pseudo)code as above, but the compiler now knows what the offset of Count should be.
You mention that there are three possibilities for the type of what element is pointing to. There are a couple of ways of handling that: either a distinguished union or an "inherited" structure. For a distinguished union use, say, a structure with one element being an enum identifying which of the three possibilities and another element being a union of the three possible structures; this is roughly what the ML languages (OCaml, Haskell, etc.) call an algebraic data type or what a union is in Pascal. For "inheritance", you could use type definitions:
In this case, you could use the changeCount function above and pass in a pointer to a counted_int_t, counted_double_t, or counted_charptr_t.
What happens is that the compiler will lay out the three structures with the Count element in the "descendant" structures in the same place as long as the counted_t element is first. (At least, in every compiler I have ever used and in every bit of code I have seen. I think this made it into the C standard at some point, but it is a very normal idiom.)
[1] Except for debugging information, if you have told the compiler to emit it. Your program will not have access to that information, though, so it would not help in this case.
[2] The x++ (postincrement) operation increments the variable (well, lvalue) that it is applied to; the assignment in the original code is unnecessary.
这正是问题所在:
按名称调用方法或成员数据通常不是静态类型语言的功能。
即使是
void *
也只能可靠地转换为T *
,其中 T 是一种类型,而指针实际上是指向该类型的指针。在C++ 中,一种可能性是让所有三种类型都继承自具有方法Count(即接口ICountable)的同一个虚拟低音类X。然后转换为
X *
并使用p->Count
。这将是惯用的 C++ - 所有类都实现相同的接口,因此它们都支持此方法。这将是一种语言支持的方法,类似于依赖 Tommy McGuire 的答案中所示的相同结构偏移技巧,这使得结构按照惯例都相似。如果您要更改结构,或者编译器要偏离结构布局标准,那么您将陷入困境。我不禁认为这只是一个玩具问题,因为该方法非常简单,通常不会将其包装在函数中 - 人们会简单地将其称为内联:
T t; t.Count++;
。This is exactly the problem:
Calling a method or member data by name is not generally a feature of staticly-typed languages.
Even a
void *
can only be reliably cast to aT *
, where T is a type, when the pointer is actually a pointer to that type.In C++, one possibility is to have all three types inherit from the same virtual bass class X which has a method Count (i.e. an interface ICountable). Then casting to
X *
and usingp->Count
. This would be idiomatic C++ - all the classes implement the same interface and thus they all support this method. This would be a language-supported method kind of analogous to relying on the same struct offsets trick shown in Tommy McGuire's answer, which makes the structs all similar by convention. If you were to alter the structs, or the compiler were to depart from the standard for laying out structs, you would be in hot water.I can't help thinking that this is rather a toy problem, since the method is so simple, one would typically not wrap it in a function - one would simply call it inline:
T t; t.Count++;
.您需要将指针强制转换为这 3 种类型之一,然后使用它。例如:
但是,您需要确定要转换的对象的类型,因为将对象转换为错误的类型可能很危险。
You need to cast the pointer to one of those 3 types and then use it. Something like:
However, you need to be sure of the type of the object you are casting as casting an object to the wrong type can be dangerous.
首先,您传递一个指向 void 的指针,这是对于未知类型的有效方法,但您需要传递指向不同对象类型的指针。
C 不是动态语言,因此符号类型信息在运行时之前大部分被删除,因此当您说您的三种类型都有一个成员
Count
时,这对您的函数设计没有帮助。访问
Count
的唯一方法是在使用->
或取消引用之前,将
(即不仅仅是void*
参数转换为正确的指针类型(*element).Count.
)。除非您依赖具有兼容布局的类型(这可能取决于实现),否则您还需要传递一些内容来帮助您的函数确定要执行的正确转换。此时,您可能会更好地使用三个独立的函数和更好的类型安全性。
First, you are passing in a pointer to void, which is a valid approach for unknown types, but you need to be passing in pointers to your different object types.
C isn't a dynamic language so symbolic type information is largely deleted before run time so when you say that your three types all have a member
Count
this doesn't help with your function design.The only way you can access
Count
is by casting yourvoid*
parameter to the correct pointer type before derefencing with->
or(*element).Count
(i.e. not just.
).Unless you are relying on your types having a compatible layout (which is likely to be implementation dependent) you will also need to pass something that helps your function determine the correct cast to perform. At this point you may be better off with three seperate functions and better type safety.
您必须将其转换为实际类型,但由于结构在同一位置没有 count 属性,您可能会得到意外的结果。
另请记住,如果您将指针传递给如下所示的结构,您将增加 x 字段而不是计数。
You have to cast it to the actual type but you may get unexpected results in the structs don't have the count property in the same place.
Also remember that if you pass a pointer to a struct that looks like the following you will increment the x field not the count.
如果
.Count
元素对于这 3 种类型中的每一种都具有相同的类型,那么您还可以使用宏。假设它是 int,你可以这样做:这会起作用,因为
a.Count
地址将在你调用函数时解析,而不是之后(当你不再知道类型时)。我假设您在调用该函数时拥有正确的类型。所以Sometype x; changeCount(x);
可以工作,但传入已经是(void*)
的内容则不行。另外,您的原始表达式
element.Count = element.Count++;
相当奇怪。如果要递增,请使用element.Count++
或element.Count = element.Count + 1
。If the
.Count
element is of the same type for each of those 3 types, then you can also use a macro. Assuming it's int, you can do this:This will work, because the
a.Count
address will be resolved when you call the function, not after (when you don't know the type anymore). I assume that you have the proper type when you call the function though. SoSometype x; changeCount(x);
will work, but passing in something that already is a(void*)
won't.Also your original expression
element.Count = element.Count++;
is rather bizarre. If you want to increment, useelement.Count++
, orelement.Count = element.Count + 1
.使用演员表。
还有你的 element.Count = element.Count++;行是多余的,你只需要执行 element.count++ (这会将值增加一)
use a cast.
also your element.Count = element.Count++; line is redundant, you just need to do element.count++(which increments the value by one)