两个本应兼容的函数指针类型被警告为不兼容。我违反了什么规则?

Two supposedly compatible function pointer types is being warned as incompatible. What rule am I breaking?

我有一组函数用于对大整数执行无符号模乘法。出于某种原因,当我在乘法中使用我的通用余数函数时,编译器(我使用带有 -Wall -Wextra 警告标志的 Clang)给出了不兼容的指针警告。

Header:

typedef struct {
    uint32_t c;
    uint32_t v[];
} vlong_t; // generic incomplete type.

vlong_t *vlong_remv_inplace(vlong_t *rem, const vlong_t *b);

typedef void *(*vlong_modfunc_t)(
    vlong_t *restrict v,
    void *restrict ctx);

vlong_t *vlong_mulv(
    vlong_t *restrict out,
    const vlong_t *a,
    const vlong_t *b,
    vlong_modfunc_t modfunc,
    void *restrict mod_ctx);

给出警告的测试代码部分:

vlong_mulv(x, a, b, vlong_remv_inplace, b);

我稍微简化了代码以创建一个最小的工作示例,我的编码风格有不同的变量命名约定。

警告如下:

vlong-test.c:85:20: warning: incompatible pointer types passing 'vlong_t *(vlong_t *, const vlong_t *)' to parameter of type 'vlong_modfunc_t'
      (aka 'void *(*)(vlong_t *restrict, void *restrict)') [-Wincompatible-pointer-types]

vlong_mulv(x, a, b, vlong_remv_inplace, b);
                    ^~~~~~~~~~~~~~~~~~

我违反了什么规则?

警告是正确的,我使用了不同的 return 和参数类型。

虽然大多数遵循 ILP32LP64IL32LLP64 的 C 调用约定要求所有指针都像 size_t 一样传递给被调用者,但标准并不排除可能的“lunati-c”实现,将指向不同类型的指针传递给可能不同的寄存器集。这与所有对象指针都可以无损地转换为 void * 并返回的事实无关。

C 标准工作草案 (n2573) 是这样说的:

6.3.2.3。 §8

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

函数vlong_remv_inplace必须与类型vlong_modfunc_t具有相同的签名所以在vlong_remv_inplace 你需要把第二个参数的类型和结果替换成空指针:

void *vlong_remv_inplace(vlong_t *rem, void *b);

另一种方法是更改​​类型的签名 vlong_modfunc_t 以匹配函数的签名 vlong_remv_inplace.

vlong_t *vlong_remv_inplace(vlong_t *rem, const vlong_t *b);

typedef void *(*vlong_modfunc_t)(
    vlong_t *restrict v,
    void *restrict ctx);

vlong_t *vlong_mulv(
    vlong_t *restrict out,
    const vlong_t *a,
    const vlong_t *b,
    vlong_modfunc_t modfunc,
    void *restrict mod_ctx);

函数 vlong_remv_implace 接受一个指向 vlong_t 的指针和一个指向 const vlong_t 的指针,而 vlong_modfunc_t 类型描述了一个指向接受受限制的函数的指针指向 vlong_t 的指针和指向非 const vlong_t 的受限指针。 constness 应该匹配(并且可能还有指针参数的 restrictness,这也是不同的)以匹配指针类型。