.下列哪些定义是有效的(即在所有系统上编译时没有错误或警告,并且在调用非常量数据时总是正确运行)?
从技术上讲,没有一个在这个意义上是有效的,因为C标准允许实现为任何东西生成警告,所以至少在概念上,有一个编译器 for each 定义发出警告.相反,我将考虑C标准是否要求对语句进行诊断.
该标准要求对约束冲突进行诊断.这里我们关心的是定义中的初始化.C 2018 6.7.9 11告诉我们,在这些初始化中,简单赋值的约束适用:
标量的初始化器应该是一个表达式,可以用大括号括起来.对象的初始值是表达式的初始值(转换后);与简单赋值相同的类型约束和转换适用,将标量的类型作为其声明类型的非限定版本.
C 2018 6.5.16.1 1列出了简单分配的限制条件.其中,唯一适用于从非空指针常量的指针向函数类型赋值的方法是:
…左操作数具有原子、限定或非限定指针类型,并且(考虑左操作数在左值转换后将具有的类型)这两个操作数都是指向兼容类型的限定或非限定版本的指针,并且左指向的类型具有右指向的类型的所有限定符;…
在每种情况下,右操作数都是函数指示符,并且该函数指示符会自动转换为指向该函数的指针.这样做的结果是一个不合格的指针.
因此,对于pointer_type f_a = function_a;
,左操作数的值为void (*)(uint8_t * data)
,右操作数的值为void (*)(uint8_t * const data)
.两者都是非限定指针,因此剩下的问题是它们是否是兼容类型.C 2018 6.7.6.1 2告诉我们:
要使两种指针类型兼容,两种类型应具有相同的限定,并且都应为指针
转换为兼容类型.
所以现在的问题是void ()(uint8_t * data)
(一种函数类型,因为内部*
已经被移除)是否与void ()(uint8_t * const data)
(同上)兼容.C 2018 6.7.6.3 15谈到了函数类型的兼容性.由于省略了与手头问题无关的部分,该公司表示:
对于两个兼容的函数类型,两者都应指定兼容的返回类型.此外,参数类型列表,如果两者都存在,应该在参数的数量上一致.;相应的参数应该具有兼容的类型.(在确定类型兼容性和复合类型时,每个用函数或数组类型声明的参数被视为具有调整的类型,每个用限定类型声明的参数被视为具有其声明类型的非限定版本.
这两种函数类型都有参数列表,每个列表都有一个参数.他们是uint8_t * data
岁和uint8_t * const data
岁.右边的是const
的合格版本,我们被告知要考虑它的不合格版本.它的不合格版本是uint8_t * data
,然后两者具有相同的类型.C 2018 6.2.7 1表示:
如果两种类型的类型是相同的…,则它们的值为compatible type
因此,pointer_type f_a = function_a;
满足约束并且不需要诊断.
对于pointer_type f_b = function_b;
,同样的分析使我们得到左边的uint8_t * data
和右边的const uint8_t * data
.它们是不同的,因此相同类型兼容的规则不适用.我们考虑上面引用的关于指针兼容性的规则.uint8_t * data
和const uint8_t * data
是指向uint8_t
和const uint8_t
的指针.C 2018 6.7.3 11适用于:
对于要兼容的两个限定类型,两者都应具有兼容类型的相同限定版本;类型限定符在说明符或限定符列表中的顺序不影响指定的类型.
这些类型不是完全限定的,因此它们不兼容.不满足初始化的约束,因此编译器必须发出诊断.
在pointer_type f_c = function_c;
年中,分析得出了同样的结论.void function_c(const uint8_t * const data)
中的后const
在6.7.6.3 15中被忽略,但我们以uint8_t const * data
和uint8_t * data
结束,它们不兼容,因此需要进行诊断.
如果指针类型的参数是常量指针或指向常量数据,它可以指向哪个函数?
正如我们已经看到的,参数(不是实参;实参是在函数调用中传递的,而不是在函数声明中声明的)是否具有const
限定符是无关紧要的,因为在考虑函数兼容性时会忽略它.
如果参数指向一个用const
限定的类型,那么它与指向一个用const
限定的类型的参数兼容,否则兼容,并且它与指向一个不用const
限定的类型的参数不兼容.
注意,这与调用函数不同:如果为指向const
类型的参数传递指向非const
类型的参数,这是允许的,因为使用了简单赋值的规则,这允许向指向的类型添加限定符.但是当一个函数指针赋值给另一个函数指针时,参数兼容性测试不允许添加限定符.