相关疑难解决方法(0)

为什么“int (*)(float)”指向“int foo()”会触发警告,而“int (*)(double)”则不会触发警告?

我有这段代码:

int foo() { return 0; }
int main()
{
    int (*float_function)(float) = foo;
}
Run Code Online (Sandbox Code Playgroud)

x86-64 GCC 12.2当使用, with编译时-Wall,它会产生警告(链接):

警告:从不兼容的指针类型“int (*)()”初始化“int (*)(float)”[-Win兼容指针类型]

但是,当我从 更改floatdouble链接)时:

int foo(){ return 0;}
int main()
{
    int (*double_function)(double) = foo;
}
Run Code Online (Sandbox Code Playgroud)

现在警告消失了。

但我认为这两者都应该受到警告。

我有什么地方说错了吗?为什么 GCC 不抱怨第二个例子?

c function-pointers language-lawyer

53
推荐指数
1
解决办法
2100
查看次数

函数指针转换为不同的签名

我使用函数指针的结构来实现不同后端的接口.签名非常不同,但返回值几乎都是void,void*或int.


struct my_interface {
    void  (*func_a)(int i);
    void *(*func_b)(const char *bla);
    ...
    int   (*func_z)(char foo);
};
Run Code Online (Sandbox Code Playgroud)

但是后端不需要支持每个接口函数的功能.所以我有两种可能性,第一种选择是在每次调用之前检查指针是否为NULL.我不太喜欢这样,因为可读性和因为我担心性能影响(但我没有测量它).另一种选择是具有虚函数,对于极少数情况,接口函数不存在.

因此,我需要为每个签名都使用一个虚函数,我想知道是否有可能只有一个用于不同的返回值.并将其转换为给定的签名.


#include <stdio.h>

int nothing(void) {return 0;}

typedef int (*cb_t)(int);

int main(void)
{
    cb_t func;
    int i;

    func = (cb_t) nothing;
    i = func(1);

    printf("%d\n", i);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用gcc测试了这段代码,它确实有效.但它是否理智?或者它可以破坏堆栈还是会导致其他问题?

编辑:感谢所有答案,经过一番深入阅读后,我现在学到了很多关于调用约定的知识.并且现在可以更好地理解引擎盖下发生的事情.

c pointers

16
推荐指数
1
解决办法
2万
查看次数

转换函数指针

我正在编写一个函数,它接收一个指向比较函数的指针和一个数组,MyStructs并且应该根据比较函数对数组进行排序:

void myStructSort(
                  struct MyStruct *arr,
                  int size,
                  int (*comp)(const struct MyStruct *, const struct MyStruct *)) {
  qsort(arr, size, sizeof(struct MyStruct), comp);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不会编译,因为qsort期望比较器接收void *参数而不是const struct MyStruct *.我想到了几个不好的解决方案,并想知道正确的解决方案是什么.

选项1

铸造compint (*)(const void *, const void*).这编译但是未定义的行为(参见这个问题).

选项2

创建一个全局变量int (*global_comp)(const struct MyStruct *, const struct MyStruct *),并设置global_comp=comp里面myStructSort.然后创建一个函数:

int delegatingComp(const void *a, const void *b) {
  return globalComp((const struct MyStruct *)a, (const …
Run Code Online (Sandbox Code Playgroud)

c function-pointers qsort

15
推荐指数
2
解决办法
1676
查看次数

如果我转换函数指针,更改参数的数量会发生什么

我刚刚开始围绕C中的函数指针.要了解函数指针的转换是如何工作的,我编写了以下程序.它基本上创建了一个函数指针,该函数指向一个带有一个参数的函数,将它转换为带有三个参数的函数指针,并调用该函数,提供三个参数.我很好奇会发生什么:

#include <stdio.h>

int square(int val){
  return val*val;
}

void printit(void* ptr){
  int (*fptr)(int,int,int) = (int (*)(int,int,int)) (ptr);
  printf("Call function with parameters 2,4,8.\n");
  printf("Result: %d\n", fptr(2,4,8));
}


int main(void)
{
    printit(square);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这编译并运行时没有错误或警告(在Linux/x86上为gcc -Wall).我系统的输出是:

Call function with parameters 2,4,8.
Result: 4
Run Code Online (Sandbox Code Playgroud)

显然,多余的论点只是默默地被抛弃了.

现在我想了解这里发生了什么.

  1. 至于合法性:如果我理解正确地将函数指针强制转换为另一种类型的答案,那么这只是未定义的行为.因此,运行并产生合理结果的事实只是纯粹的运气,对吗?(或编译器编写者的好看)
  2. 为什么gcc不会警告我这个,即使是Wall?这是编译器无法检测到的东西吗?为什么?

我来自Java,那里的类型检查要严格得多,所以这种行为让我有点困惑.也许我正在经历文化冲击:-).

c pointers casting function-pointers type-safety

10
推荐指数
2
解决办法
2562
查看次数

调用具有比预期更多参数的C函数是否安全?

我反复讨论在GTK +代码中设置信号处理程序的问题,不需要几个参数,并试图使用与处理程序相同的函数来处理多个信号,其处理程序具有不同的签名 - 但是使用前N个参数(我关心的那些)同样的.

它是否安全(在某种意义上它不是未定义的行为,而不是更实用的"它在我的PC上工作吗?")将指针传递给GObject API,当这些函数期望参数少于它们时实际上是从信号发射过程中获得的?

或者,将此与GTK +离婚,这段代码还可以吗?

/* Note: No void *userdata argument! */
void show(int x) {
  printf("x = %d\n", x);
}

void do_stuff(void (*fn)(int, void *), void *userdata) {
  static int total = 0;
  (*fn)(total, userdata);
  total++;
}

void doitnow(void) {
  do_stuff(&show, NULL);
}
Run Code Online (Sandbox Code Playgroud)

要获得额外的功劳,请讨论功能签名和呼叫站点之间不同返回值类型的含义.

编辑:一个几乎相同的问题更密切地探测"兼容的函数类型",并直接解决我的具体问题 - 链接GObject信号处理程序.TL; DR:是的,它是未定义的行为,但它在某些工具包中实际上是惯用的(尽管不是强制性的).

c gtk gobject

9
推荐指数
1
解决办法
656
查看次数

reinterpret_cast to void*不使用函数指针

我想重新解释将函数指针强制转换为void*变量.函数指针的类型将是类型Class* (*)(void*).

以下是示例代码,

class Test
{
    int a;
};

int main()
{
    Test* *p(void **a);
    void *f=reinterpret_cast<void*>(p);     
}
Run Code Online (Sandbox Code Playgroud)

上面的代码适用于Visual Studio/x86编译器.但是使用ARM编译器,它会产生编译错误.不知道为什么.

错误:#694:reinterpret_cast无法抛弃const或其他类型的限定符

我在将函数指针转换为另一种类型时阅读了解释

我担心下面的解释.

在函数指针和常规指针之间进行转换(例如,将a转换void (*)(void)为a void*).函数指针的大小不一定与常规指针相同,因为在某些体系结构中它们可能包含额外的上下文信息.这可能在x86上运行正常,但请记住它是未定义的行为.

如何void (*)(void*) -> void*有效地进行这样的转换,以至于它在大多数编译器中编译几乎相同?

c++ arm casting

6
推荐指数
2
解决办法
8133
查看次数

这个C语法是什么意思?

这来自我正在使用的"神奇"数组库.

void
sort(magic_list *l, int (*compare)(const void **a, const void **b))
{
    qsort(l->list, l->num_used, sizeof(void*),
         (int (*)(const void *,const void *))compare);
}
Run Code Online (Sandbox Code Playgroud)

我的问题是:究竟什么是qsort做的最后一个论点?

(int (*)(const void *, const void*))compare) 
Run Code Online (Sandbox Code Playgroud)

qsort采用int (*comp_fn)(const void *,const void *)比较器参数,但此sort函数采用带双指针的比较器.不知何故,上面的行将双指针版本转换为单指针版本.有人可以解释一下吗?

c syntax function-pointers qsort

6
推荐指数
2
解决办法
968
查看次数

使用不同指针类型作为参数来转换函数指针

我想,以下代码描述了我想要做的事情.具体来说,我希望将函数指针强制转换为泛型函数类型,唯一的区别在于签名是不同的指针类型.

现在,我知道要求函数指针兼容,本问题所述,但我不确定是否有不同指针类型的参数满足兼容性要求.

代码编译并运行,但是,正如预期的那样,从不兼容的指针类型给出有关赋值的警告.有没有办法满足编译器并实现我的目标?

#include <stdio.h>

int float_function(float *array, int length)
{
    int i;
    for(i=0; i<length; i++){
        printf("%f\n", array[i]);
    }
}

int double_function(double *array, int length)
{
    int i;
    for(i=0; i<length; i++){
        printf("%f\n", array[i]);
    }
}


int main() 
{
    float a[5] = {0.0, 1.0, 2.0, 3.0, 4.0};    
    double b[5] = {0.0, 1.0, 2.0, 3.0, 4.0};

    int (*generic_function)(void*, int) = NULL;

    generic_function = &float_function;
    generic_function(a, 5);

    generic_function = &double_function;
    generic_function(b, 5);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

c function-pointers

6
推荐指数
1
解决办法
6226
查看次数

有没有办法传递带泛型参数的函数指针?

我正在实现一个通用的单链表,其中列表节点存储指向其数据的指针.

typedef struct sll_node
{   
    void *data;
    struct sll_node *next;
} sll_node;
Run Code Online (Sandbox Code Playgroud)

为了实现一个适用于任何类型数据的通用查找子例程,我编写了它,以便它将一个函数指针作为参数作为参数,如下所示:

/* eq() must take 2 arguments. ex: strcmp(char *, char *) */
sll_node *sll_find(void *data, int (*eq)(), sll_node *root);
Run Code Online (Sandbox Code Playgroud)

您可以传递适用于手头数据类型的函数指针.因此,如果在列表节点中存储字符串,则可以将strcmp作为eq()函数传递,依此类推.它有效,但我仍然不满意..

有没有办法明确指定比较函数参数的数量而不放弃它的一般性?

我一开始尝试了这个:

sll_node *sll_find(void *data, int (*eq)(void *, void *), sll_node *root);
Run Code Online (Sandbox Code Playgroud)

我希望它能起作用.但是没有(编辑:它编译了一个警告,但我有 - 错误!),我不得不围绕strcmp编写一个包装函数,使其符合eq原型.

然后我尝试了:

sll_node *sll_find(void *data, int (*eq)(a, b), sll_node *root);
Run Code Online (Sandbox Code Playgroud)

要么:

typedef int (*equality_fn)(a, b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);
Run Code Online (Sandbox Code Playgroud)

这两个都不会编译,因为:"只有函数定义才允许没有类型的参数列表"

c function-pointers

6
推荐指数
1
解决办法
1044
查看次数

传递`int(*)(char const*)`其中`int(*)(char*)`是预期的

我有一个函数指针,其函数被声明为期望char *参数.对于它,我想保存一个指向声明为接受char const*参数的函数的指针.

我想我可以使用包装器或演员.强制转换似乎更直接,但我可以合法地调用这样的函数指针的结果吗?

示例代码如下:

static int write_a(char * X){
    return 0;
}

static int write_b(char const* X){
    return 0;
}
static int wrapped_write_b(char * X){
    return write_b(X);
}

typedef int (*write_fn)(char * );

write_fn a = write_a;
write_fn b = wrapped_write_b;
write_fn b1 = (write_fn)write_b; //is b1 legally callable?
Run Code Online (Sandbox Code Playgroud)

c pointers function-pointers

6
推荐指数
2
解决办法
246
查看次数