C中的变量参数,如何获取generics类型的值?

我正在尝试使用带有generics类型的C stdarg.h lib。 int类型,是我的generics类型>要理解它,请继续阅读。 所以,我的问题是:

我有一个接受可变数量参数的函数。 喜欢

void function (int paramN, ...); 

在我的程序中,没有办法知道,哪个是变量参数的类型,它可以是char,数组,int,short,函数点等…就像

 function (paramN, "Hey, I'm a string", 1, function_pint, array, -1); // -1 is a sentinel. 

所以,我认为,一个int,是32位,在x86(32位)系统中,这将保存所有内存地址。 所以,如果我用int得到所有参数,它就不会有问题,例如,“嘿,我是一个字符串”这个字符串的地址,通常适合32位变量,所以,我只需要制作演员。

我是正确的?
我可以做吗?
注意:我不想让我的函数像printf(这个解决方案,在这种情况下不适合好吗?)

谢谢你的回答。
抱歉我的英语不好。

你不能像你描述的那样去做。

C调用约定是让调用者将参数放在堆栈上,但它不会在类型上放置任何信息,因此被调用者必须有办法找到它(至少是变量的大小)。

  • 对于每种类型的原型函数都没有问题。

  • 对于具有可变数字或参数(可变参数)的函数,它更棘手,您必须为每个参数调用va_arg来读取每个变量,并且必须为va_arg提供类型。 如果你提供的类型不是真正的类型,编译器不会抱怨(它不能是运行时信息),但任何事情都可能发生(通常是坏事)。

因此你必须通过这种类型。

在某些情况下,您可以预测类型(例如:一个计数器后跟一些整数,依此类推),但通常您将其传递给参数编码。 你可以传递它像printf那样的格式字符串编码,jldupont描述的联合技巧也很常见。

但无论如何你必须通过它。

你真的不能依赖底层数据二进制表示,甚至数据大小。 即使程序在编写时似乎有效,它也没有兼容性,并且可以在系统,编译器或更改编译选项的任何更改时中断。

让我们来看一个示例,其中传递类型后跟参数(因此既不是联合技巧也不是像printf那样的格式字符串)。 它的作用是将所有传递的值转换为double并添加它们,没有真正有用的不是它:

 #include  #include  enum mytypes {LONG, INT, FLOAT, DOUBLE }; double myfunc(int count, ...){ long tmp_l; int tmp_i; double tmp_d; double res = 0; int i; va_list ap; va_start(ap, count); for(i=0 ; i < count; i++){ int type = va_arg(ap, enum mytypes); switch (type){ case LONG: tmp_l = va_arg(ap, long); res += tmp_l; break; case INT: tmp_i = va_arg(ap, int); res += tmp_i; break; case FLOAT: /* float is automatically promoted to double when passed to va_arg */ case DOUBLE: tmp_d = va_arg(ap, double); res += tmp_d; break; default: /* unknown type */ break; } } va_end(ap); return res; } int main(){ double res; res = myfunc(5, LONG, (long)1, INT, (int)10, DOUBLE, (double)2.5, DOUBLE, (double)0.1, FLOAT, (float)0.3); printf("res = %f\n", res); } 

此示例使用C99中定义的 stdarg可变参数头。 要使用它,你需要为你的函数至少有一个固定的参数(在这个例子中它是count )。 好的,如果这样你可以在你的函数中有几个可变参数列表(例如myfunc(int count1, ..., int count2, ...) )。 坏的是你不能拥有纯粹的变量函数(比如myfunc(...)就像旧的格式一样。你仍然可以使用旧的格式使用varargs兼容头。但它更复杂,很少需要,因为你需要类型,但也有一些方法来知道列表已经完成,像count这样的东西很方便(但不是唯一的方法,例如,可以使用'终止符')。

对每个参数使用void *(或类型化结构)并使用带有“type”参数(整数)的结构。 用于包含实际值的指针/联合。

换句话说,每个参数都传递一个指向类型化结构的指针。 此类型结构的每个实例都包含一个值。 此“值”的类型包含在此类型结构中。

更好的例子:

 typedef struct { int type; union { int int_value; double double_value; ... }; } Param; 

void function(Param *p1, Param *p2, ...)

我遇到的这种技巧的最新例子是DBus。

您不能以您描述的方式使用变量参数执行此操作,因为除非您明确地执行此操作,否则在编译后不会保留有关所传递的参数类型的信息。 即使传递参数变量的地址也不会告诉你这一点,因为表示字符串的内存中的位可以表示数字或其他内容。

要使varargs使用变量类型,您可以在参数本身中存储类型信息(例如,如jldupont在其答案中所述),或者您可以将信息存储在非变量参数中(例如,像printf这样的格式字符串) S)。

来自http://en.wikipedia.org/wiki/Stdarg.h的回复 :

没有定义用于确定传递给函数的未命名参数的类型的机制。 该函数只需要以某种方式知道或确定它,其方法各不相同。

那就是你的函数不能只从参数中知道哪个参数是字符串。 你需要以某种方式告诉你的function会发生什么。 这就是printf惯例如此常见的原因。

试试u?intptr_t,它在stdint.h中定义。 这是一个整数,保证大到足以容纳指针。

您可能还想考虑传递浮点数时会发生什么; 它被转换为double并作为double传递。 如果你的程序期望一个int,这将破坏它的堆栈视图。 哎哟。 并且编译器无法捕捉到这一点。

并且你的函数,如定义的那样(禁止paramN中的任何位编码,在这种情况下它应该是枚举或位域,或者至少是无符号的)无法知道它接收的参数类型是什么,以及这样的人不可能对他们做任何有用的事情。 C没有关于其对象的运行时类型信息。

你到底想要做什么?