如果I typedef是以下形式的函数指针:

typedef void (*pointer_type)(uint8_t * data);

并定义以下形式的一些函数:

void function_a(uint8_t * const data) {}
void function_b(const uint8_t * data) {}
void function_c(const uint8_t * const data) {}

以下哪些定义是有效的(即在所有系统上编译时都不会出现错误或警告,并且在调用非常数数据时始终正确运行)?

pointer_type f_a = function_a;
pointer_type f_b = function_b;
pointer_type f_c = function_c;

反过来呢?如果指针类型的参数是一个常量指针或指向常量数据,它可以指向哪个函数?

我试着用谷歌搜索这一点,但输入"函数指针"和"常量"这两个词就会被常量函数指针如何工作的解释淹没.基于C标准的解释以及C标准版本之间的任何相关差异将不胜感激.

推荐答案

.下列哪些定义是有效的(即在所有系统上编译时没有错误或警告,并且在调用非常量数据时总是正确运行)?

从技术上讲,没有一个在这个意义上是有效的,因为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 * dataconst uint8_t * data是指向uint8_tconst 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 * datauint8_t * data结束,它们不兼容,因此需要进行诊断.

如果指针类型的参数是常量指针或指向常量数据,它可以指向哪个函数?

正如我们已经看到的,参数(不是实参;实参是在函数调用中传递的,而不是在函数声明中声明的)是否具有const限定符是无关紧要的,因为在考虑函数兼容性时会忽略它.

如果参数指向一个用const限定的类型,那么它与指向一个用const限定的类型的参数兼容,否则兼容,并且它与指向一个不用const限定的类型的参数不兼容.

注意,这与调用函数不同:如果为指向const类型的参数传递指向非const类型的参数,这是允许的,因为使用了简单赋值的规则,这允许向指向的类型添加限定符.但是当一个函数指针赋值给另一个函数指针时,参数兼容性测试不允许添加限定符.

C++相关问答推荐

有什么方法可以检测SunOS上的SparcWorks吗?

如何在C中只使用一个带双方括号([i][j])访问语法的malloc来分配动态大小的2d数组?

如何一次获取一个字符

难以理解Makefile隐含规则

C语言中的strstr问题

在C语言中,在数学运算过程中,为什么浮点数在变量中的行为不同

为静态库做准备中的奇怪行为

在为hashmap创建加载器时,我的存储桶指向它自己

Sizeof(&Q;字符串&Q;)的正确输出是什么?

如何用c语言修改shadow文件hash部分(编程)?

OpenSSL:如何将吊销列表与SSL_CTX_LOAD_VERIFY_LOCATIONS一起使用?

如何在GET_STRING输入后对少数几个特定字符串进行C判断?

循环中的静态变量与块中的变量和循环

安全倒计时循环

如何在不读取整个字符串的情况下删除UTF8字符串的尾随空格以提高性能?

c程序,让用户输入两类数字,并给出输出用户输入多少个数字

C中的char**v*char[]

如何在MSVC中使用intSafe.h函数?

如何使crc32的结果与cksum匹配?

如何修复数组数据与列标题未对齐的问题?