【问题标题】:Why is there an error while declaring a generic pointer to a function?为什么在声明指向函数的泛型指针时会出错?
【发布时间】:2020-09-06 11:57:33
【问题描述】:

在执行此代码时,我在ptr = (void* (void*, void*))sumx; 行得到[Error] cast specifies function type

我正在尝试声明一个通用指针 ptr 指向一个名为 sumx 的函数

任何帮助将不胜感激。谢谢。

# include <stdio.h>

int sum(int a, int b) {
    return a+b;
}

int* sumx(int *a, int *b) {
    int *p;
    *p = *a+*b;
    return p;
}

int main() {
    int (*p) (int, int);
    void* (*ptr)(void*, void*);

    p = sum;
    ptr = (void* (void*, void*))sumx;


    int s = (*p)(5, 6);

    int a = 10;
    int b = 20;
    int *y = (*ptr)(&a, &b);

    printf("%d\n", s);
    printf("%d\n", *y);
    return 0;
}

【问题讨论】:

  • sumx() 返回int*.. 应该是int* (*ptr)(void*, void*);

标签: c function pointers function-pointers


【解决方案1】:

你在演员阵容中缺少(*),应该是这样的:

ptr = ( void* (*)(void*, void*) )sumx;

但是,在使用此类函数指针时,通常使用typedef 语句会更清晰:

#include <stdio.h>
#include <stdlib.h>

int sum(int a, int b)
{
    return a + b;
}

int* sumx(int* a, int* b)
{
    int* p = malloc(sizeof(int)); // Need to allocate some memory
    *p = *a + *b;
    return p;
}

typedef int (*ifncptr)(int, int);
typedef void* (*vpfncptr)(void*, void*);

int main()
{
    ifncptr p;
    vpfncptr ptr;
    p = sum;
    ptr = (vpfncptr)sumx;

    int s = p(5, 6); // You can just use the fn-ptr for this call ...

    int a = 10;
    int b = 20;
    int* y = ptr(&a, &b); // ^ ... and here, also!

    printf("%d\n", s);
    printf("%d\n", *y);
    free(y); // Don't forget to free the memory.
    return 0;
}

【讨论】:

  • typedef void* (*fncptr)(void*, void*); --> typedef int* (*fncptr)(int*, int*);
  • 那又怎样,用警告编译void *(*ptr)(void *, void*) = sumx;会得到:warning: initialization from incompatible pointer type
  • typedef void* (*vpfncptr)(void*, void*); 是您正在使用的签名,不是吗?所以是的,您正在这样做。你编译过警告吗? -Wall -pedantic?
  • @DavidRanieri 使用直接初始化 (vpfncptr ptr = sumx;) 会发出警告,但使用 explicit cast (作为我的代码和 OP 的)不会!
  • 当然不是,演员隐藏了警告但问题仍然存在:)
【解决方案2】:
int* sumx(int *a, int *b) {
    int *p;
    *p = *a+*b;
    return p;
}

您正在写入一个未初始化的指针,使用malloc 保留空间:

    int *p = malloc(sizeof(*p));

并回答您的问题:

void* (*ptr)(void*, void*);

不是sumx 的有效签名,而是:

int *(*ptr)(int *,  int*);

【讨论】:

  • “通用”指针类型使用clang-cl 编译(一旦添加了缺少的(*))。但是对于sumx 中未分配的p 来说是个好地方。
  • 我和@David 在一起不是因为对齐问题,而是因为更清洁的解决方案。老实说,我无法想到这种奇怪的演员阵容有用的场景。我宁愿定义正确的函数指针并只转换结果(但是AFAIK不需要转换为void *,所以......)。
  • @RobertoCaboni 即,使用 void *(*ptr)(void *, void*) = sumx; 编译并带有警告给出:warning: initialization from incompatible pointer type
  • @RobertoCaboni 可能有使用这种“通用”指针的情况;例如,许多WinAPI 调用返回一个通用的FARPROC 函数指针,正如我在这里思考的那样:stackoverflow.com/q/57973305/10871073(尽管Q 是关于C++)。
【解决方案3】:

为什么在声明指向函数的泛型指针时会出错?

因为在 C 语言中不存在指向函数的通用指针之类的东西。 void* 是一个指向 object(变量)的通用指针。 void* 一般只能与对象指针一起使用,而不能与函数指针一起使用。

然而,可以在一个函数指针到另一个函数指针之间进行转换,但是当您调用实际函数时,您必须通过正确的函数指针类型来执行此操作。


您还有其他各种错误,例如未分配内存等,这些错误已由其他答案解决。但是让我们忽略所有这些,以及在这种情况下并不能真正满足目的的函数指针的使用。您真正想做的事情可以通过现代标准 C 以下列方式实现:

#include <stdio.h>

int sum_int(int a, int b) {
  return a + b;
}

int sum_intp(const int *a, const int *b) {
  return *a + *b;
}

#define sum(a,b) ( (void)_Generic((b), int:0, int*:0),/*NOTE: comma operator*/ \
  _Generic((a),  \
  int:  sum_int, \
  int*: sum_intp)(a,b) )

int main(void) 
{
  int a=10;
  int b=20;
  printf("%d\n", sum(a, b));
  printf("%d\n", sum(&a, &b));
  //printf("%d\n", sum(a, &b)); //compiler error
  //printf("%d\n", sum((float)a, b)); //compiler error
  return 0;
}

C11 _Generic 宏在编译时确定类型。宏的伪代码为:

  • 检查参数 b 是否为有效类型。
  • 丢弃该参数的值 b 检查 - 它只是为了在类型错误的情况下生成编译器错误。
  • 逗号运算符。
  • "检查参数a是否也有效,然后根据a的类型调用正确的函数。

这是类型安全的,不需要void* 或函数指针。

【讨论】:

  • 你能解释一下(void)_Generic((b), int:0, int*:0),如何检查参数b是否是一个有效的类型吗?
  • @DavidRanieri 如果参数b 既不是int 也不是int*,您将收到类似于“不匹配通用关联列表中的任何类型”的编译器错误。请注意缺少default:。如果匹配,则表达式返回一个虚拟0,然后将其与(void) 一起丢弃。为了保证两个参数的类型安全,我必须使用逗号运算符这样做。
  • 哦,我明白了,如果没有提供这些类型,那么在没有default 的情况下它会扩展为(void),,不是吗?非常好的技巧!
  • @DavidRanieri 从技术上讲,逗号运算符的左操作是一个空表达式,但 gcc/clang 认为左表达式没有任何意义并相应地发出警告。 (void) 强制转换严格来说是不需要的,它只是为了让编译器满意。
【解决方案4】:

主要问题

您实际上不能在表达式中使用函数。在表达式中使用函数时,它会自动转换为指向该函数的指针。1

这意味着您的强制转换不应尝试转换为 void *(void *, void *) 之类的函数类型,而应转换为 void *(*)(void *, void *) 之类的函数指针类型。

两个问题

C 允许将函数指针转换为不同类型的函数指针,但是,在调用函数时,用于调用的指针应遵守与实际函数定义兼容的某些规则。将参数或返回类型从 int * 更改为 void * 不遵守这些规则。因此,对于您拥有的类型,(*ptr)(&amp;a, &amp;b) 不是正确的调用。

sumx中,p没有在*p = *a + *b;之前初始化,所以不能指望它指向任何东西。您应该初始化它,可能使用p = malloc(sizeof *p);。调用malloc 后,您应该测试结果。如果它等于NULL,您的程序应该打印一条错误消息并退出或以其他方式处理分配内存失败。

脚注

1 有两个例外。一种是当您使用&amp; 手动获取函数的地址时,没有自动转换。另一个是sizeof 应用于函数时没有转换,但随后存在约束冲突。 C 标准没有定义将sizeof 应用于函数的行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-10-28
    • 2011-06-20
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    相关资源
    最近更新 更多