将通用函数指针转换为带有指向不同类型的 void* 参数的特定签名是否定义良好

问题描述 投票:0回答:1

在下面的模拟示例中,我尝试构造一个

generic_processor
接口,其中调用者将对象指针传递到处理器中的
void*
参数中,并且调用者显式地将“动作”转换为
void(*gfp)(void)
(通用函数指针),这正是
generic_processor
所期望的。

“具体操作”对于跨类型的每个操作具有几乎相同的签名,因为它们采用对象指针,可能还有一些其他参数并返回

int
。请注意,具体操作采用具体类型的对象指针。

这是关键步骤,我对此表示怀疑:

generic_processor

gfp
参数转换为函数指针
typedef
,其签名与具体操作函数相匹配
,除了对象指针现在是一个void*

正如这个答案中引用的:

https://stackoverflow.com/a/189126/1087626

    766 指向一种类型函数的指针可以转换为指向另一种类型函数的指针,然后再转换回来;
  • 767 结果应等于原始指针。
  • 768 如果使用转换后的指针来调用其类型与所指向类型不兼容的函数,则行为未定义。
所以,问题的核心是:在这种情况下“不兼容”是什么意思?

generic_processor

 中,我正在转换为函数指针签名,该签名与原始具体签名相同,只是它具有 
void*
 作为第一个参数,而不是 
circle*
square*

这个定义清楚吗?

模拟示例:

#include <stdio.h> // types typedef struct { int a; double d; } circle; typedef struct { int b; float f; } square; // ... 10 types,.. different sizeof() // concrete API int open_circle(circle* c) { printf("opening circle: %d: %f\n", c->a, c->d); return c->a; } int open_square(square* s) { printf("opening square: %d: %f\n", s->b, s->f); return s->b; } int send_circle(circle* c, const char* msg) { printf("sending circle: %d: %f: %s\n", c->a, c->d, msg); return -c->a; } int send_square(square* s, const char* msg) { printf("sending square: %d: %f: %s\n", s->b, s->f, msg); return -s->b; } // ten more operations for each type // "genericised" function pointer types (note the void* params!!) typedef int (*open_fpt)(void* o); typedef int (*send_fpt)(void* o, const char*); typedef void (*gfp)(void); // generic function pointer int generic_processor(void* obj, gfp open, gfp send) { int sum = 0; sum += ((open_fpt)open)(obj); sum += ((send_fpt)send)(obj, "generically sent"); return sum; } int main() { circle c = {2, 22.2}; square s = {3, 33.3F}; int net = 0; net += generic_processor(&c, (gfp)open_circle, (gfp)send_circle); net += generic_processor(&s, (gfp)open_square, (gfp)send_square); printf("net %d\n", net); return 0; }
用 gcc 13.2 编译:工作正常,没有警告:

gcc -std=c99 -g -o gfp tests/gfp.c -Wall -Wextra -pedantic -fsanitize=address,leak,undefined && ./gfp opening circle: 2: 22.200000 sending circle: 2: 22.200000 opening square: 3: 33.299999 sending square: 3: 33.299999 net 0
    
c generics language-lawyer function-pointers
1个回答
0
投票
忽略可变参数函数和旧的 K&R 风格声明,如果两个函数指针指向以下函数,则它们是兼容的:

    他们的返回类型是相同的
  • 它们具有相同数量的参数
  • 参数类型相同
在您的情况下,您的函数都与函数指针

open_fpt

send_fpt
 兼容,因为它们采用单个 
void *
 参数,并且相关函数采用指向不同结构类型的单个指针。  所以这个转换是无效的并导致未定义的行为。

对象指针和函数指针之间的转换也是未定义的。 特别是,当需要

generic_processor

 参数时,您不能将函数指针传递给 
void *
,也不能在函数中进行另一个方向的转换。

您需要做的是更改函数以采用

void *

 参数,然后将参数转换为正确的类型。  例如:

int open_circle(void *p) { circle* c = p; printf("opening circle: %d: %f\n", c->a, c->d); return c->a;
并且您需要更改 

generic_processor

 以采用函数指针类型:

int generic_processor(void* obj, open_fpt open, send_fpt send) { int sum = 0; sum += open(obj); sum += send(obj); return sum; }
    
© www.soinside.com 2019 - 2024. All rights reserved.