2013-01-03 145 views
11

虽然遵循一些教程并阅读了关于函数指针的知识,但我明白,在ISO C中显式地将一个void指针分配给函数指针是未定义的,有什么方法可以解决我在编译时收到的警告(例如更好的编码方式)还是应该忽略它?ISO C Void *和函数指针

警告:

ISO C forbids assignment between function pointer and 'void *' [-pedantic] 

例如代码:

void *(*funcPtr)(); 
funcPtr = GetPointer(); 

GetPointer是返回空指针E.G.的函数

void *GetPointer(); 
+3

如何给一个函数不分配一个空指针指针? –

+4

它在理论上是未定义的,因为一些机器曾经有不同的代码地址和数据地址大小。在实践中,在当今最常见的架构中,代码和数据地址在相同的地址空间中具有相同的大小。 –

+0

GetPointer()返回什么?第二行是你得到错误的地方吗? –

回答

6

号编译器是正确的,你也:在C89和C99,你无法将数据指针之间进行转换(这void *是)和函数指针,因此对于解决警告的唯一方法是返回函数的一个函数指针。 (注意,实际上尽管有警告,它仍然可以工作,即使在标准库中有这种不一致 - dlsym()函数用于获取函数指针,但它返回void * - 所以基本上可以忽略警告。它会工作,虽然严格来说这里的行为是不明确的。)

+0

事实上,我只是想了解更好的编码风格(因此为什么我使用这种严格的警告进行编码),我只是有兴趣了解它,而不是关于没有警告和更多关于理解它们的原因,影响和可能避免它们的方法。 –

+0

@Tomwaivory是的,我明白了,这绝对是一种很好的做法。在这种情况下,语言是错误的:)所以,不要担心,练习与理论不一样,除非你在使用DS9k,它可以在这个UB里面工作,并且你可以跳动C标准委员会的成员用一根棍子:D – 2013-01-03 07:00:06

+0

谢谢你很高兴知道,我不能修复语言只是尽我所能对它编码:) –

2

我用glib遇到了这个问题。 Glib数据结构(如GSList)通常具有一个名为void * data的字段。我想存储功能列表,并得到了类似这样的一堆错误:

warning: ISO C forbids passing argument 2 of ‘g_slist_append’ between function pointer and ‘void *’ [-pedantic] 

本例使用产生一堆警告的gcc -Wall -ansi -pedantic

typedef int (* func) (int); 

int mult2(int x) 
{ 
    return x + x; 
} 

int main(int argc, char *argv[]) 
{ 
    GSList *functions = NULL; 
    func f; 

    functions = g_slist_append(functions, mult2); 
    f = (func *) functions->data; 
    printf("%d\n", f(10)); 
    return 0; 
} 

所以我包在结构中的功能和所有的警告消失:

struct funcstruct { 
    int (* func) (int); 
}; 

int mult2(int x) 
{ 
    return x + x; 
} 

int main(int argc, char *argv[]) 
{ 
    GSList *functions = NULL; 
    struct funcstruct p; 
    p.func = mult2; 

    functions = g_slist_append(functions, &p); 
    p = * (struct funcstruct *) functions->data; 
    printf("%d\n", p.func(10)); 
    return 0; 
} 

这是值得商榷的,这是相当多的额外的代码,使一些警告消失,但我不喜欢我的代码生成的警告。另外,以上是玩具的例子。在我正在编写的真实代码中,事实证明,将函数列表包装在结构中非常有用。

我很想知道这是否有问题,或者是否有更好的方法。

+0

如果你这样做,你不能使用'p'范围以外的列表,因为指向'p'的指针只在'p'范围内有效。所以你的名单将是无用的。并且使结构类型完全没有必要。如果你简单地将'p'声明为'int(* p)(int);'它的工作方式与你现在拥有的几乎相同。 – newacct

5

tlpi-book我发现这招很有意思:

#include <dlfcn.h> 

int 
main(int argc, char *argv[]) 
{ 
    ... 
    void (*funcp)(void);  /* Pointer to function with no arguments */ 
    ... 
    *(void **) (&funcp) = dlsym(libHandle, argv[2]); 
} 
+0

有人可以解释它是如何工作的更多细节? –

+1

@MatthieuPoullet:在右侧,我们有一个void指针('void *')。在左边,我们取一个函数指针的地址。暂时忽略'(void **)',指向函数指针的指针被最左边的'*'解除引用。 '(void **)'只是说指向一个函数指针的指针应该被转换为一个指向void指针的指针,当它被最左边的'*'解除引用时,就是一个'void *'。因此,赋值的左侧和右侧都有'void *'类型,编译器很高兴。 – mtk

0

基于@WeinanLi答案,但使用的辅助功能为清楚:

#include <dlfcn.h> 

/* Pointer to function with no arguments */ 
typedef void (functor_t*)(void); 

void load_symbol(functor_t* functor, void* dl_handle, const char* symbol_name) { 
    *(void**)functor = dlsym(dl_handle, symbol_name); 
} 

int 
main(int argc, char *argv[]) 
{ 
    // [...] 
    functor_t funcp; 
    // [...] 
    load_symbol(&funcp, libHandle, argv[2]); 
}