C 这两种指针类型真的不兼容吗?

C 这两种指针类型真的不兼容吗?,c,pointers,gcc,function-pointers,C,Pointers,Gcc,Function Pointers,我有以下typedef和更高阶函数: typedef void block_fn (struct block*, block_sector_t, void* buffer); static void block_apply (struct page* page, block_fn *block_fn) { int i = 0; for (i = 0; i < PAGE_SECTORS; i++) { block_fn(swap.device, page->secto

我有以下
typedef
和更高阶函数:

typedef void block_fn (struct block*, block_sector_t, void* buffer);

static void block_apply (struct page* page, block_fn *block_fn) {
  int i = 0;
  for (i = 0; i < PAGE_SECTORS; i++) {
    block_fn(swap.device, page->sector * PAGE_SECTORS + i, page_kpage(page)
             + i * BLOCK_SECTOR_SIZE);
  }
}
block\u read
block\u write
具有以下签名:

void block_read (struct block *, block_sector_t, void *);
void block_write (struct block *, block_sector_t, const void *);
这导致GCC抱怨:

../../vm/swap.c:94:13:注意:应为
void(*)(结构块*,块扇区,void*)
但参数类型为
void(*)(结构块*,块扇区,常量void*)

GCC生气对吗?以这种方式传递函数指针安全吗


编辑:我知道这两个函数指针是不同类型的。我的问题是以这种方式使用它们是否安全:它会触发未定义的行为吗?是否存在这样的情况:以这种方式传递不兼容类型的函数指针会导致意外情况发生?

是的,一般来说,抱怨是对的,但在您的情况下没有意义

通过说const void*,您声明函数不会修改 指向数据。省略const,就是说它可能

函数签名是调用者和被调用者之间的契约,由编译器执行 强制执行。编译器防止代码公布比它更严格的签名 实现(例如,在写入只读参数时播发只读参数)

不过,在您的例子中,您只需要一个指向具有有限类型检查的函数的指针

您可以将该类型声明为taking const void*,并使用类型转换重写该const
当您调用它时。

这两种指针类型不兼容


在我看来,以这种方式传递函数指针是不安全的

更高级别的代码知道由于其函数签名,
block\u apply()
可以处理什么类型。要将不同的类型传递给
block_apply()
,需要做两件事:1)更高级别的代码破坏签名(强制转换),2)更高级别的代码知道可以这样做

最好不要强制执行较高级别的代码,并减轻它了解
block_apply()
内部工作原理的负担


为了避免强制转换,代码可以创建另一个函数
block\u apply\u const()
。然后根据需要调用
block\u apply(page,&block\u read)
block\u apply\u const(&block\u write)

typedef void block_fn_const (struct block*, block_sector_t, const void* buffer);

static void block_apply_const (struct page* page, block_fn_const *block_fn) {
   // same code
}
但这重复了代码

或者,包装器函数可以执行强制转换。这比使用强制转换调用
block\u apply()
来丢弃更高级别的代码要好。由于强制转换应该谨慎地进行,并且这些
静态
函数知道可以执行强制转换,所以这里的代码可以执行

static void block_apply_const (struct page* page, block_fn_const *block_fn) {
   block_apply(page, (block_fn) block_fn);
}

为什么不将此函数调用更改为并尝试一下呢?无效块写入(结构块*,块扇区,无效*);我不认为GCC在抱怨你的函数指针。它抱怨您试图将
void*
变为
const void*
。是的,它是。如果不是这样的话,这种区别就没有意义了;对吧?嗯。。。请问您为什么要重新定义
void
??这不仅会给每个读者造成巨大的混乱,而且很可能容易出错。重新定义原语和伪原语总是一个非常糟糕的主意,即使它可以编译。相反,应该为函数指针类型指定一个别名。我猜你还没有查过关于
typedef
的规范。@请说明
typedef
是一种函数指针类型,而不是重新定义
void
。最好通过
void*
获取它,并在需要时添加
const
。这样比较安全,不太安全。目前,有两个函数签名,一个带有
const void*
,另一个带有
void*
,编译器会抱怨哪一个不合适。您的提案在writer函数中强制至少一次强制转换。走另一条路意味着没有强制转换。等等,你的意思是改变block_read的原型吗?那就行了。我建议强制转换block_apply()并更改block_fn typedef,并保持读写器函数不变。是的。总是避免使用强制类型转换,它们散发出危险的味道。“虽然在您的情况下没有意义”——这是否意味着这种不兼容的指针总是可以安全使用的?这个标准有保证吗?编译器可以执行哪些优化来破坏此代码?“依我看,以这种方式传递此函数指针是不安全的。”--编译器可以执行哪些优化来导致此代码的意外行为?这真的是我的问题。它真的不安全吗?@Patrick Collins 1)说你的机器使用(用于图片2014)。然后,指向常量内存的指针和指向普通内存的指针可以具有相同的值,但指向不同的位置(ROM与RAM)。您的代码和我的第二个解决方案都失败了,因为指向常量内存和动态内存的指针不能简单地强制转换。我的第一个“//相同代码”的解决方案会奏效。更多2)正如@Deduplicator所说,在施放时有危险的气味。创建一组函数时,调用例程不应执行强制转换来欺骗函数。任何技巧,如果有的话,都应该发生在套房内。
static void block_apply_const (struct page* page, block_fn_const *block_fn) {
   block_apply(page, (block_fn) block_fn);
}