函数指针的可变函数
variadic function to function pointer
有没有办法像这样将可变参数函数转换为函数指针,合法吗?我同时询问 C 和 C++,但由于两种语言都存在这种结构,所以我将这两个问题放在一个问题中。
extern int test(int, ...);
auto testptr = (int(*)(int, int, long)) &test;
谢谢,
杰克
C 风格的可变参数函数是它们自己的特殊野兽。它们的参数必须用 va_list
函数族解包。与非可变参数函数没有兼容性,它们不是宏,也不是。
考虑到您对 auto
的使用,我假设您询问的是 C++,尽管有双重标记。 Try to avoid functions like that in C++.
您可以自由地将函数指针转换为不同类型的函数指针,但通过不同类型的函数指针调用函数是未定义的行为。 (所以你可以用转换函数指针做的唯一有效的事情就是将它转换回正确的类型。)
可变参数函数与具有特定数量参数的函数不是同一类型。所以转换的结果不能用来调用函数。
如果您在某些编译器上尝试它,您可能会发现它可以达到您的预期。但这并不能使它有效;它可能会在不同的平台或编译器版本上崩溃。
(以上对C和C++都适用。)
在 C 中,这些类型不兼容。
C standard 的第 6.2.7 节关于 "Compatible type and composite type" 说明了以下关于函数指针兼容性的内容:
3 A composite type can be constructed from two types that are compatible; it is a type that is compatible with both of the two types
and satisfies the following conditions:
- If both types are array types, the following rules are applied:
- If one type is an array of known constant size, the composite type is an array of that size.
- Otherwise, if one type is a variable length array whose size is specified by an expression that is not evaluated, the
behavior is undefined.
- Otherwise, if one type is a variable length array whose size is specified, the composite type is a variable length array of
that size.
- Otherwise, if one type is a variable length array of unspecified size, the composite type is a variable length array of unspecified
size.
- Otherwise, both types are arrays of unknown size and the composite type is an array of unknown size. The element type of
the composite type is the composite type of the two element
types.
- If only one type is a function type with a parameter type list (a function prototype), the composite type is a function
prototype with the parameter type list.
- If both types are function types with parameter type lists, the type of each parameter in the composite parameter type list is the
composite type of the corresponding parameters.
...
5 EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]);
int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);
第 6.7.6.3p15 节指出:
For two function types to be compatible, both shall specify
compatible return types. Moreover, the parameter type
lists, if both are present, shall agree in the number of
parameters and in use of the ellipsis terminator;
corresponding parameters shall have compatible types. If one type
has a parameter type list and the other type is specified by a
function declarator that is not part of a function definition
and that contains an empty identifier list, the parameter list
shall not have an ellipsis terminator and the type of each parameter
shall be compatible with the type that results from the
application of the default argument promotions. If one type has a
parameter type list and the other type is specified by a function
definition that contains a (possibly empty) identifier list, both
shall agree in the number of parameters, and the type of
each prototype parameter shall be compatible with the type
that results from the application of the default argument
promotions to the type of the corresponding identifier. (In
the determination of type compatibility and of a composite
type, each parameter declared with function or array type is
taken as having the adjusted type and each parameter declared with
qualified type is taken as having the unqualified version of its
declared type.)
在你的例子中:
int test(int, ...);
此函数与以下兼容:
int (*)(); // a function taking an unknown number of parameters and returns an int
int (*)(int, ...); // a function taking an int and variable parameters after and returns an int
但不是:
int (*)(int, int, long); // a function taking an int, an int, and a long, and returns an int
因为两种函数类型都指定了一个参数列表,而且因为参数个数和省略号的使用都不一致,所以类型不兼容。尝试通过不兼容的指针调用函数会根据第 6.3.2.3p8 节调用 undefined behavior:
A pointer to a function of one type may be converted to a pointer to a
function of another type and back again; the result shall compare
equal to the original pointer. If a converted pointer is used to call
a function whose type is not compatible with the referenced type, the
behavior is undefined.
有没有办法像这样将可变参数函数转换为函数指针,合法吗?我同时询问 C 和 C++,但由于两种语言都存在这种结构,所以我将这两个问题放在一个问题中。
extern int test(int, ...);
auto testptr = (int(*)(int, int, long)) &test;
谢谢, 杰克
C 风格的可变参数函数是它们自己的特殊野兽。它们的参数必须用 va_list
函数族解包。与非可变参数函数没有兼容性,它们不是宏,也不是。
考虑到您对 auto
的使用,我假设您询问的是 C++,尽管有双重标记。 Try to avoid functions like that in C++.
您可以自由地将函数指针转换为不同类型的函数指针,但通过不同类型的函数指针调用函数是未定义的行为。 (所以你可以用转换函数指针做的唯一有效的事情就是将它转换回正确的类型。)
可变参数函数与具有特定数量参数的函数不是同一类型。所以转换的结果不能用来调用函数。
如果您在某些编译器上尝试它,您可能会发现它可以达到您的预期。但这并不能使它有效;它可能会在不同的平台或编译器版本上崩溃。
(以上对C和C++都适用。)
在 C 中,这些类型不兼容。
C standard 的第 6.2.7 节关于 "Compatible type and composite type" 说明了以下关于函数指针兼容性的内容:
3 A composite type can be constructed from two types that are compatible; it is a type that is compatible with both of the two types and satisfies the following conditions:
- If both types are array types, the following rules are applied:
- If one type is an array of known constant size, the composite type is an array of that size.
- Otherwise, if one type is a variable length array whose size is specified by an expression that is not evaluated, the behavior is undefined.
- Otherwise, if one type is a variable length array whose size is specified, the composite type is a variable length array of that size.
- Otherwise, if one type is a variable length array of unspecified size, the composite type is a variable length array of unspecified size.
- Otherwise, both types are arrays of unknown size and the composite type is an array of unknown size. The element type of the composite type is the composite type of the two element types.
- If only one type is a function type with a parameter type list (a function prototype), the composite type is a function prototype with the parameter type list.
- If both types are function types with parameter type lists, the type of each parameter in the composite parameter type list is the composite type of the corresponding parameters.
...
5 EXAMPLE Given the following two file scope declarations:
int f(int (*)(), double (*)[3]); int f(int (*)(char *), double (*)[]);
The resulting composite type for the function is:
int f(int (*)(char *), double (*)[3]);
第 6.7.6.3p15 节指出:
For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types. If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions. If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)
在你的例子中:
int test(int, ...);
此函数与以下兼容:
int (*)(); // a function taking an unknown number of parameters and returns an int
int (*)(int, ...); // a function taking an int and variable parameters after and returns an int
但不是:
int (*)(int, int, long); // a function taking an int, an int, and a long, and returns an int
因为两种函数类型都指定了一个参数列表,而且因为参数个数和省略号的使用都不一致,所以类型不兼容。尝试通过不兼容的指针调用函数会根据第 6.3.2.3p8 节调用 undefined behavior:
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.