将一个带有 const 指针参数的函数指针转换为等效函数但具有非常量指针参数可以吗?
Is casting a function pointer that takes a const pointer argument to the equivalent function but with non constant pointer arguments OK?
我创建了 2 个函数,用于读取和写入路径,声明如下:
int Read(const char * /*Filename*/, void * /*Ptr*/, size_t /*Size*/), Write(const char * /*Filename*/, const void * /*Ptr*/, size_t /*Size*/);
我创建了一个附加函数,它将使用路径调用上述函数之一
static int IOData(int(*const Func)(const char *, void *, size_t)) {
char Filename[DATA_PATH_LEN];
// Build path
return Func(Filename, &Data, sizeof(Data));
}
但是,当 Write
作为回调传递给 IOData
时,编译器会引发以下警告
Incompatible pointer types passing 'int (const char *, const void , int)' to parameter of type 'int ()(const char *, void *, int)'
将接受 const 指针的函数转换为接受非 const 指针的函数是否为未定义行为?
我注意到有一个 但那个问题使用 C++ 但是这个问题使用纯 C 所以使用模板不是一个选项
这是不允许的,因为相应参数之一的类型不兼容。
兼容类型在 C standard:
的第 6.2.7p1 节中定义
Two types have compatible type if their types are the same. Additional
rules for determining whether two types are compatible are described
in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in
6.7.6 for declarators. ...
第 6.7.3p10 节详细说明了合格类型的兼容性:
For two qualified types to be compatible, both shall have the
identically qualified version of a compatible type; the order of type
qualifiers within a list of specifiers or qualifiers does not affect
the specified type.
这意味着 const void *
和 void *
不兼容。
功能类型的兼容性在第 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.)
所以因为对应的一组参数不兼容,所以函数类型也不兼容
最后,关于函数调用运算符的第 6.5.2.2p9 节 ()
描述了这种情况下发生的情况:
If the function is defined with a type that is not compatible with the
type (of the expression) pointed to by the expression that denotes the
called function, the behavior is undefined.
因此通过不兼容的函数指针类型调用函数会触发未定义的行为,因此不应该这样做。
该标准试图将其行为可能无法在某些合理的实现上定义的任何行为归类为未定义行为。因为将一个动作分类为 UB 绝不会损害质量实现的能力,作为“一致的语言扩展”,当一个动作存在时,以一种有用的普通方式处理该动作,所以没有必要避免将其特征化为 UB 动作大多数实现将以相同有用的方式进行处理。
尝试静态确定最大堆栈使用率的实现可能合理地假设调用具有特定签名的函数指针将仅调用其地址被占用且其签名完全匹配的函数。如果标准要求指向此类函数的指针可以互换,那可能会无可挽回地破坏静态分析工具以前能够容纳的程序。
没有理由期望质量实现不应该配置为将此类函数指针视为可互换的,在这样做有用和实用的情况下,但该标准放弃了对实现质量问题的管辖权有用性和实用性。不幸的是,可能很难知道应该依赖哪些实现来支持此类构造,因为许多没有理由不支持此类构造的实现并不认为它们支持它们这一事实足以证明显式的合理性文档。
我创建了 2 个函数,用于读取和写入路径,声明如下:
int Read(const char * /*Filename*/, void * /*Ptr*/, size_t /*Size*/), Write(const char * /*Filename*/, const void * /*Ptr*/, size_t /*Size*/);
我创建了一个附加函数,它将使用路径调用上述函数之一
static int IOData(int(*const Func)(const char *, void *, size_t)) {
char Filename[DATA_PATH_LEN];
// Build path
return Func(Filename, &Data, sizeof(Data));
}
但是,当 Write
作为回调传递给 IOData
时,编译器会引发以下警告
Incompatible pointer types passing 'int (const char *, const void , int)' to parameter of type 'int ()(const char *, void *, int)'
将接受 const 指针的函数转换为接受非 const 指针的函数是否为未定义行为?
我注意到有一个
这是不允许的,因为相应参数之一的类型不兼容。
兼容类型在 C standard:
的第 6.2.7p1 节中定义Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers, and in 6.7.6 for declarators. ...
第 6.7.3p10 节详细说明了合格类型的兼容性:
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type; the order of type qualifiers within a list of specifiers or qualifiers does not affect the specified type.
这意味着 const void *
和 void *
不兼容。
功能类型的兼容性在第 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.)
所以因为对应的一组参数不兼容,所以函数类型也不兼容
最后,关于函数调用运算符的第 6.5.2.2p9 节 ()
描述了这种情况下发生的情况:
If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.
因此通过不兼容的函数指针类型调用函数会触发未定义的行为,因此不应该这样做。
该标准试图将其行为可能无法在某些合理的实现上定义的任何行为归类为未定义行为。因为将一个动作分类为 UB 绝不会损害质量实现的能力,作为“一致的语言扩展”,当一个动作存在时,以一种有用的普通方式处理该动作,所以没有必要避免将其特征化为 UB 动作大多数实现将以相同有用的方式进行处理。
尝试静态确定最大堆栈使用率的实现可能合理地假设调用具有特定签名的函数指针将仅调用其地址被占用且其签名完全匹配的函数。如果标准要求指向此类函数的指针可以互换,那可能会无可挽回地破坏静态分析工具以前能够容纳的程序。
没有理由期望质量实现不应该配置为将此类函数指针视为可互换的,在这样做有用和实用的情况下,但该标准放弃了对实现质量问题的管辖权有用性和实用性。不幸的是,可能很难知道应该依赖哪些实现来支持此类构造,因为许多没有理由不支持此类构造的实现并不认为它们支持它们这一事实足以证明显式的合理性文档。