我有这段代码:
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兼容指针类型]
但是,当我从 更改float
为double
(链接)时:
int foo(){ return 0;}
int main()
{
int (*double_function)(double) = foo;
}
Run Code Online (Sandbox Code Playgroud)
现在警告消失了。
但我认为这两者都应该受到警告。
我有什么地方说错了吗?为什么 GCC 不抱怨第二个例子?
我使用函数指针的结构来实现不同后端的接口.签名非常不同,但返回值几乎都是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测试了这段代码,它确实有效.但它是否理智?或者它可以破坏堆栈还是会导致其他问题?
编辑:感谢所有答案,经过一番深入阅读后,我现在学到了很多关于调用约定的知识.并且现在可以更好地理解引擎盖下发生的事情.
我正在编写一个函数,它接收一个指向比较函数的指针和一个数组,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
铸造comp
到int (*)(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中的函数指针.要了解函数指针的转换是如何工作的,我编写了以下程序.它基本上创建了一个函数指针,该函数指向一个带有一个参数的函数,将它转换为带有三个参数的函数指针,并调用该函数,提供三个参数.我很好奇会发生什么:
#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)
显然,多余的论点只是默默地被抛弃了.
现在我想了解这里发生了什么.
我来自Java,那里的类型检查要严格得多,所以这种行为让我有点困惑.也许我正在经历文化冲击:-).
我反复讨论在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:是的,它是未定义的行为,但它在某些工具包中实际上是惯用的(尽管不是强制性的).
我想重新解释将函数指针强制转换为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)
为avoid*
).函数指针的大小不一定与常规指针相同,因为在某些体系结构中它们可能包含额外的上下文信息.这可能在x86上运行正常,但请记住它是未定义的行为.
如何void (*)(void*) -> void*
有效地进行这样的转换,以至于它在大多数编译器中编译几乎相同?
这来自我正在使用的"神奇"数组库.
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函数采用带双指针的比较器.不知何故,上面的行将双指针版本转换为单指针版本.有人可以解释一下吗?
我想,以下代码描述了我想要做的事情.具体来说,我希望将函数指针强制转换为泛型函数类型,唯一的区别在于签名是不同的指针类型.
现在,我知道要求函数指针兼容,如本问题所述,但我不确定是否有不同指针类型的参数满足兼容性要求.
代码编译并运行,但是,正如预期的那样,从不兼容的指针类型给出有关赋值的警告.有没有办法满足编译器并实现我的目标?
#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) 我正在实现一个通用的单链表,其中列表节点存储指向其数据的指针.
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)
这两个都不会编译,因为:"只有函数定义才允许没有类型的参数列表"
我有一个函数指针,其函数被声明为期望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)