检查函数指针是否已初始化

时间:2018-04-10 12:09:41

标签: c function-pointers

如何检查函数指针是否已初始化? 我可以检查NULL,但如果不是null可能是垃圾,对吗?

我有以下内容:

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

typedef struct client_struct
{
    char *name;
    char *email;
    void (*print)(struct client_struct *c);
} client;

void print_client(client *c)
{

    if (c->print != NULL)
        c->print(c);
}

int main()
{
    client *c = (client *)malloc(sizeof(client));
    c->email = (char *)malloc(50 * sizeof(char));
    sprintf(c->email, "email@server.com");
    c->name = (char *)malloc(50 * sizeof(char));
    sprintf(c->name, "some name");

    //Uncommenting line below work as expected, otherwise segmentation fault
    //c->print = NULL;

    print_client(c);

    printf("\nEOF\n");
    int xXx = getchar();

    return 0;
}

如何检查此指针是否真正指向函数“void(* f)(client *)”? 比较大小不起作用,因为相同大小的垃圾可以正确吗? 我希望有一种方法可以根据C标准来实现这一目标。

5 个答案:

答案 0 :(得分:1)

注意事项

检查是否使用有效函数初始化函数指针不是一个容易解决的问题。任何解决方案,可以跨平台移植,并且还依赖于最终的二进制格式(静态或动态可链接格式)。对于不同的二进制格式,有很多方法可以做到这一点,并且取得了不同的成功,但是我不打算重复每一个排列。希望这会让你走下那个兔子洞:-)然后你可以找出适合你的特殊解决方案。

为了使某些解决方案能够正常工作,您必须确保链接的二进制文件具有导出的符号(它可以在没有符号的情况下执行,但它更难以实现,而且我不会这样做有时间)。因此,当您关联程序时,请确保已启用动态符号。

话虽如此,这是一种可以在使用dlfcn函数的系统上使用的方法。 (见下面的历史)

更多警告

正如@Deduplicator在下面的评论中指出的那样,可能会出现0xdeadbeef可能随意发生指向有效函数的情况,在这种情况下,您最终可能会遇到最终调用< em>错误的有效功能。有一些方法可以在编译时或运行时缓解这种情况,但您必须手动构建解决方案。例如,C ++通过在命名空间中将命名空间修改为符号来实现它。你可能要求这样的事情发生。 (我会想到一个有趣的方法来做这个并发布它)

Linux / SysV变体(包括Mac OSX)

使用dladdr(SysV)(GNU也有一个dladdr1)来确定你提供的地址属于哪个函数

实施例

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


int is_valid_function_ptr( void *func)  {   
      Dl_info info;  
      int rc;      

      rc = dladdr(func, &info);   

      if (!rc) {
          /* really should do more checking here */
          return 0;
      }  

      return 1; /* you can print out function names and stuff here */    
}

void print(const char *value) {
    fprintf(stdout, "%s", value);
}

void call_the_printer(void (*foo)(), const char *value)
{
    if(is_valid_function_ptr(foo)) {
        foo(value);
    }
    else {
        fprintf(stderr, "The Beef is Dead!\n");
    }
}

int main() 
{
    void (*funcptr)() = (void (*)()) 0xdeadbeef; /* some dead pointer */
    call_the_printer(funcptr, "Hello Dead Beef\n");
    funcptr = print; /* actually a function */
    call_the_printer(funcptr, "Hello Printer\n");
    return 0;
}

注意启用动态符号以使其生效

GCC / LLVM等

在链接过程中使用-rdynamic-Wl,--export-dynamic,因此请编译:

  

gcc -o ex1 -rdynamic ex1.c

Windows做了自己的事情(一如既往),我还没有测试过这些,但基本概念应该有效:

同时使用GetModuleHandleEnumCurrentProcess获取加载的符号信息,并在循环中运行指针,看它们是否与其中的任何地址匹配。

另一种方法是使用VirtualQuery然后将mbi.AllocationBase转换为(HMODULE)并查看是否获得了自己的二进制文件的路径。

答案 1 :(得分:0)

在c函数中,指针与常规指针没什么区别,按标准,它们有一个值表示不应该使用该值,这是NULL

使用指针的方法是将它们仅设置为有效值或NULL。没有其他方法可以确定有一个OK值。根据定义,每个非NULL的值都应视为有效。

答案 2 :(得分:0)

与其他评论和答案中指出的一样,没有办法检查变量是否已初始化。这就是为什么将vars初始化为NULL然后检查被认为是一种良好的做法。

如果你真的想验证函数指针是否指向正确的位置,你可以导出函数并从ELF符号加载指针(参见:http://gcc.gnu.org/wiki/Visibility

答案 3 :(得分:0)

如评论中所述,不可能100%确定指针是否为垃圾。

为了避免这种情况,你可以提供一个&#34;构造函数&#34;功能,像这样:

struct client_struct* client_allocate()
{
    struct client_struct* object = malloc(sizeof *object);
    if (object)
    {
        object->name = NULL;
        object->email = NULL;
        object->print = NULL;
    }
    return object;
}

然后在您的文档中写下创建&#34;客户&#34;的唯一有效方法。是通过使用您的功能。如果您这样做,则还应提供destroy功能,并在其中致电free

假设您有一天向struct添加新指针。然后更新client_allocate函数,将此指针设置为NULL,新指针将始终正确初始化。无需更新分配了struct的代码中的所有位置,因为现在只有一个这样的位置。

答案 4 :(得分:-1)

首先检查空参数。

void print_client(client *c) 
{
    if ((c != NULL) && (c->print != NULL)) 
    {
        c->print(c);
    }   
}

至于你的问题,在你的客户端结构被malloc命令后,取消它。这样,您可以确保未分配的函数指针确实== NULL。

client* create_client(void)
{
    client *c = malloc(sizeof(client));
    if (c != NULL)
    {
        memset(c, 0, sizeof(c))
    }

    return c;
}