我创建了两个函数,用于读取和写入路径,声明如下:
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
时,编译器会发出以下警告
不兼容的指针类型将"int(const char*,const void,int)"传递到类型为"int()(const char*,void*,int))"的参数
将接受常量指针的函数强制转换为接受非常量指针的函数是否为未定义行为?
我注意到有一个几乎相同的问题,但那个问题使用C++,但这个问题使用纯C,所以使用模板不是的选项
这是不允许的,因为其中一个相应参数的类型不兼容。
兼容类型的定义见C标准第6.2.7p1节:
如果两个类型的类型相同,则它们具有兼容的类型。附加的描述了用于确定两种类型是否兼容的规则6.7.2中用于类型说明符,6.7.3中用于类型限定符,以及6.7.6对于申报人。。。
第6.7.3p10节详细介绍了合格类型的兼容性:
对于要兼容的两个合格类型,两者都应具有兼容类型的相同限定版本;类型的顺序说明符或限定符列表中的限定符不影响指定的类型。
这意味着const void *
和void *
不兼容。
功能类型的兼容性在第6.7.6.3p15:节中进行了描述
对于要兼容的两种函数类型,两者都应指定兼容返回类型此外,参数类型列表,如果两者都是目前,应在参数数量和使用方面达成一致省略号终止符;相应的参数应具有兼容性类型如果一个类型有参数类型列表,而另一个类型是由不是函数一部分的函数声明符指定定义,并且包含一个空的标识符列表,参数列表中不应有省略号终止符和每个的类型参数应与默认参数提升的应用程序。如果一种类型具有参数类型列表,另一个类型由函数指定包含(可能为空)标识符列表的定义,两者应在参数数量和每个参数的类型方面达成一致原型参数应与产生的类型兼容从默认参数promotion的应用程序到对应的标识符。(在确定类型时兼容性和复合类型,每个参数都用函数或数组类型被视为具有调整后的类型,并且使用限定类型声明的参数被视为具有其声明类型的不合格版本。)
因此,由于一组相应的参数不兼容,因此函数类型不兼容。
最后,关于函数调用运算符()
的第6.5.2.2p9节描述了这种情况下发生的情况:
如果函数定义的类型与表示称为函数,行为是未定义的。
因此,通过不兼容的函数指针类型调用函数会触发未定义的行为,因此不应该这样做。
标准试图将任何行为在某些看似合理的实现中定义可能不切实际的行为归类为未定义行为。因为将行动分类为UB决不会损害高质量实施的能力;符合语言扩展";,以一种有用的常见方式处理操作。当存在操作时,无需避免将其定性为UB操作,大多数实现都会以同样有用的方式处理这些操作。
试图静态确定最大堆栈使用率的实现可能会合理地假设,对具有特定签名的函数指针的调用只会调用其地址被占用且其签名完全匹配的函数。如果标准要求指向这些函数的指针是可互换的,这可能会不可挽回地破坏静态分析工具以前能够容纳的程序。
没有理由期望质量实现不应该被配置为在有用和实用的情况下将这些函数指针视为可互换的,但该标准放弃了对有用和实用性的实现质量问题的管辖权。不幸的是,很难知道应该依赖哪些实现来支持这些构造,因为许多没有理由不支持这些构造的实现并不认为它们支持这些构造这一事实足够值得注意,从而证明了显式文档的合理性。