当前位置: 首页 > 知识库问答 >
问题:

将一个常量指针参数转换为等价函数但带有非常量指针参数的函数指针可以吗?

公羊奇
2023-03-14

我创建了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时,编译器会发出以下警告

将“int(const char*,const void,int)”传递给类型为“int()(const char*,void*,int)”的参数的指针类型不兼容

将接受常量指针的函数转换为接受非常量指针的函数会是未定义的行为吗?

我注意到有一个几乎相同的问题,但这个问题使用C,但这个问题使用普通C,所以使用模板不是一个选项

共有2个答案

佟翰林
2023-03-14

该标准试图将任何行为归类为未定义行为,这些行为在一些看似合理的实现上定义起来可能不切实际。因为将一个动作归类为UB绝不会损害一个高质量实现的能力,即作为一个“一致的语言扩展”,当一个动作存在时,以一种有用的普通方式处理这个动作,所以没有必要避免将大多数实现都会以同样有用的方式处理的动作归类为UB动作。

试图静态确定最大堆栈使用率的实现可能会合理地假设对具有特定签名的函数指针的调用只会调用地址被占用且签名完全匹配的函数。如果标准要求指向这些函数的指针是可互换的,这可能会不可挽回地破坏静态分析工具以前能够容纳的程序

没有理由期望质量实现不应该被配置为在有用和实用的情况下将这些函数指针视为可互换的,但是标准放弃了对有用性和实用性的实现质量问题的管辖权。不幸的是,很难知道应该依赖哪些实现来支持这样的结构,因为许多没有理由不支持这样的结构的实现并不认为它们支持它们的事实值得足够注意,从而证明显式留档是合理的。

璩俊雅
2023-03-14

这是不允许的,因为其中一个相应参数的类型不兼容。

兼容类型在C标准第6.2.7p1节中定义:

如果两种类型的类型相同,则它们具有兼容的类型。6.7.2中描述了确定两种类型是否兼容的其他规则,6.7.3中描述了类型限定符,6.7.6中描述了声明符。。。

第6.7.3p10节详细说明了合格类型的兼容性:

对于要兼容的两种合格类型,两者都应具有兼容类型的相同合格版本;类型限定符在说明符或限定符列表中的顺序不影响指定的类型。

这意味着const void*void*不兼容。

第6.7.6.3p15节描述了功能类型的兼容性:

对于要兼容的两种功能类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用上保持一致;相应参数应具有兼容类型。如果一种类型有一个参数类型列表,而另一种类型由一个函数声明符指定,该函数声明符不属于函数定义的一部分,并且包含一个空的标识符列表,则参数列表不应具有省略号终止符,并且每个参数的类型应与应用默认参数所产生的类型兼容。如果一种类型具有参数类型列表,而另一种类型由包含(可能为空)标识符列表的函数定义指定,则两种类型的参数数量应一致,并且每个原型参数的类型应与应用默认参数提升到相应标识符类型所产生的类型兼容。(在确定类型兼容性和复合类型时,使用函数或数组类型声明的每个参数被视为具有调整的类型,使用限定类型声明的每个参数被视为具有其声明类型的非限定版本。)

因此,由于一组相应的参数不兼容,函数类型也不兼容。

最后,第6.5.2.2p9节关于函数调用操作符()描述了这种情况下的情况:

如果函数定义的类型与表示被调用函数的表达式指向的类型(表达式的类型)不兼容,则行为未定义。

因此,通过不兼容的函数指针类型调用函数会触发未定义的行为,因此不应该这样做。

 类似资料:
  • 主要内容:用数组作函数参数在C语言中,函数的参数不仅可以是整数、小数、字符等具体的数据,还可以是指向它们的 指针。用指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。 像数组、字符串、动态分配的内存等都是一系列数据的集合,没有办法通过一个参数全部传入函数内部,只能传递它们的指针,在函数内部通过指针来影响这些数据集合。 有的时候,对于整数、小数

  • 问题内容: 我有以下功能: 其中,的类型是具有以下定义的接口: 题: 这是真的,和是 通过按引用 ,并且有它的价值被复制? 我认为: 是通过引用的,因为它是一张地图 是一个结构。因此,我应该传递指针以避免复制数据 问题答案: 接口类型只是一组方法。请注意,接口定义的成员未指定接收方类型是否为指针。这是因为 值类型的方法集是其关联的指针类型的方法集的子集 。满嘴 我的意思是,如果您具有以下条件: 然

  • 一个函数总是占用一段连续的内存区域,函数名在表达式中有时也会被转换为该函数所在内存区域的首地址,这和数组名非常类似。我们可以把函数的这个首地址(或称入口地址)赋予一个 指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是 函数指针。 函数指针的定义形式为: returnType (*pointerName)(param list); returnType

  • Windows 10、Visual Studio 2019、C 17: 编译错误:无法将参数2从“int(\u cdecl*)(int)”转换为“…” 如果没有模板(int func(int size)),代码编译正常。如何修改它以使编译器理解可变参数?

  • 在下面的代码中,当函数print_uart传输字符串“hello world”时,*s中到底收到了什么?是字符H还是存储字符串“hello world”的内存地址?

  • 本文向大家介绍指针数组、数组指针、函数指针的区别?相关面试题,主要包含被问及指针数组、数组指针、函数指针的区别?时的应答技巧和注意事项,需要的朋友参考一下 指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在32 位系统下任何类型的指针永远是占4 个字节。它是“储存指针的数组”的简称。 数组指针:首先它是一个指针,它指向一个数组。在32