2016-04-15 340 views
7

我有一个函数,它接受一个字符串,一个字符串数组和一个指针数组,然后查找字符串数组中的字符串,并从指针数组中返回相应的指针。由于我使用了这种方法来处理几个不同的事情,所以指针数组被声明为(void *)的一个数组,并且调用者应该知道实际存在的指针类型(以及它返回的返回值是什么类型的指针)。为什么我不能将函数指针转换为(void *)?

当我通过一个函数指针数组,但是,我得到一个警告,当我编译-Wpedantic

铛:

test.c:40:8: warning: assigning to 'voidfunc' (aka 'void (*)(void)') from 'void *' converts 
     between void pointer and function pointer [-Wpedantic] 

GCC:

test.c:40:8: warning: ISO C forbids assignment between function pointer and ‘void *’ [-Wpedantic] 
    fptr = find_ptr("quux", name_list, (void **)ptr_list, 

这里有一个测试文件,该文件尽管没有警告正确打印“QUUX”:

#include <stdio.h> 
#include <string.h> 

void foo(void) 
{ 
    puts("foo"); 
} 

void bar(void) 
{ 
    puts("bar"); 
} 

void quux(void) 
{ 
    puts("quux"); 
} 

typedef void (* voidfunc)(void); 

voidfunc ptr_list[] = {foo, bar, quux}; 

char *name_list[] = {"foo", "bar", "quux"}; 

void *find_ptr(char *name, char *names[], void *ptrs[], int length) 
{ 
    int i; 

    for (i = 0; i < length; i++) { 
    if (strcmp(name, names[i]) == 0) { 
     return ptrs[i]; 
    } 
    } 
    return NULL; 
} 

int main() { 
    voidfunc fptr; 

    fptr = find_ptr("quux", name_list, (void **)ptr_list, 
        sizeof(ptr_list)/sizeof(ptr_list[0])); 
    fptr(); 

    return 0; 
} 

有没有什么办法来解决警告,比不-Wpedantic编译或复制我的find_ptr功能,一旦函数指针和一次非函数指针等?有没有更好的方法来实现我想要做的事情?

+0

IIRC问题是代码指针可能不一定与数据指针的大小相同。在大多数架构中,它们都是。我将把它留给其他人来找到对C标准的引用。您可能可以通过'uintptr_t'投射并返回来获得您想要的结果。 – abligh

+1

由于@abligh提到可能的大小不匹配,因此它是未定义的行为,可以将函数指针转换为其他指针类型。有关更多详细信息,请参阅此问题:http://stackoverflow.com/questions/13696918/c-cast-void-pointer-to函数指针 – erdeszt

+2

什么让你不能用'voidfunc * ptrs'来代替'void * ptrs []'? – alk

回答

7

你不能修复警告。事实上,在我看来,它应该是一个硬错误,因为将函数指针转换为其他指针是非法的,因为今天有体系结构,这不仅违反了C标准,而且会导致代码的实际错误不行。编译器允许这样做,因为许多体系结构都会忽略它,即使这些程序在某些其他体系结构上会崩溃。但这不仅仅是一个理论标准的违规行为,它还会导致真正的错误。

例如在IA64函数指针(或至少曾经是我最后一次看)实际上是两个值,都需要使跨越共享库或程序和共享库函数调用。同样,通常的做法是将函数指针转换并调用,将函数的指针返回给指向返回void的函数的指针,因为无论如何您都会忽略返回值在ia64上也是非法的,因为这会导致陷阱值泄漏到寄存器中稍后在许多不相关的代码中导致崩溃。

请勿投射函数指针。始终让他们匹配类型。这不仅仅是标准跋涉,这是一个重要的最佳实践。

+0

很好的答案,但我有一个问题:将指针存储在void(* f)()内部的(任意)函数是否安全并在进行调用时将其转换为适当的类型? – HolyBlackCat

+2

@HolyBlackCat从我所知道的今天存在的架构来看,是的。从标准的角度来看,没有。从未来的兼容性角度来看,我宁愿不要。从你打破的公司的角度来看,去做吧。看看你能逃脱的一个好方法是看大型项目。例如,当编译器对某些东西严格要求时,Linux内核就会中断,你可以确信你会听到它,至少会有标志来禁用它。硬件体系结构也是如此,很少有人会在今天建立一个Linux无法运行的CPU。 – Art

+1

[This answer](http://stackoverflow.com/a/5579907/149138)与你的相矛盾。特别是,它引用了标准,该标准指示函数指针可以转换为任何其他函数指针类型并返回。 – BeeOnRope

5

一个解决方案是添加一个间接级别。这有助于很多事情。不是存储指向函数的指针,而是存储指向存储指向函数的指针的指针struct

typedef struct 
{ 
    void (*ptr)(void); 
} Func; 

Func vf = { voidfunc }; 

ptrlist[123] = &vf; 

+3

你不需要'struct';你可以使用指针指针。 – davmac

+1

@davmac是的,但你可以添加更多的东西到结构中,并建立一个穷人的封闭。 –

1

经过一些修改可以避开指针对话:

#include <stdio.h> 
#include <string.h> 

void foo(void) 
{ 
    puts("foo"); 
} 

void bar(void) 
{ 
    puts("bar"); 
} 

void quux(void) 
{ 
    puts("quux"); 
} 

typedef void (* voidfunc)(void); 

voidfunc ptr_list[] = {foo, bar, quux}; 

char *name_list[] = {"foo", "bar", "quux"}; 

voidfunc find_ptr(char *name, char *names[], voidfunc ptrs[], int length) 
{ 
    int i; 

    for (i = 0; i < length; i++) { 
     if (strcmp(name, names[i]) == 0) { 
      return ptrs[i]; 
     } 
    } 
    return NULL; 
} 

int main() { 
    voidfunc fptr; 

    fptr = find_ptr("quux", name_list, ptr_list, 
        sizeof(ptr_list)/sizeof(ptr_list[0])); 
    fptr(); 

    return 0; 
} 
+0

我不想将指针数组参数声明为函数指针,因为我对其他东西也使用了相同的函数。 – sagittarian

5

这是一件早已在C标准打破,从来没有固定的 - 有可用于没有通用的指针类型指向函数的指针和指向数据的指针。

在C89标准之前,所有C编译器都允许在不同类型的指针之间进行转换,并且char *通常用作通用指针,可能指向任何数据类型或任何函数。C89增加了void *,但是在条款中只有对象指针可以转换为void *,而不需要定义对象是什么。尽管如此,还是有很多代码将函数指针转换为void *,并期望它能正常工作。因此,几乎所有的C编译器仍然允许它,并且仍然生成正确的代码,因为任何编译器都不会被拒绝为不可用。

严格地说,如果你想在C通用指针,你需要定义一个可容纳工会无论是void *void (*)()并调用之前使用函数指针的显式转换为正确的函数指针类型它。

相关问题