繁体   English   中英

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

[英]Check if a pointer to function is initialized

如何检查指向函数的指针是否已初始化? 我可以检查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标准来实现的方法。

注意事项

检查指向函数的指针是否已使用有效函数初始化不是一个容易解决的问题。 任何解决方案都不能跨平台移植,并且还取决于最终使用的二进制格式(静态或动态可链接格式)。 有多种方法可以在不同的二进制格式上取得不同的成功,但是我不会遍历所有排列。 希望这会让您步入困境:-),然后您可以找出适合您情况的特定解决方案。

为了使某些解决方案起作用,您必须确保链接的二进制文件已导出符号(可以不使用它而做,但是这样做要困难得多,而且我没有时间)。 因此,在链接程序时,请确保已启用动态符号。

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

更多警告

就像@Deduplicator在下面的评论中指出的那样,在某些情况下,可能会任意发生0xdeadbeef指向有效函数的情况,在这种情况下,您可能最终会遇到调用错误的有效函数的情况。 有一些方法可以在编译时或运行时减轻这种情况,但是您必须手动构建解决方案。 例如,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 -rdynamic -Wl,--export-dynamic ,因此请使用以下命令进行编译:

gcc -o ex1 -rdynamic ex1.c

视窗

Windows做自己的事情(一如既往),并且我还没有测试过其中任何一个,但是基本概念应该起作用:

一起使用GetModuleHandleEnumCurrentProcess可获得加载的符号信息,并在循环中遍历指针以查看它们与其中的任何地址匹配。

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

在c函数中,指针与常规指针没有什么不同,按照标准,它们有一个值表示不应使用该值,并且该值为NULL

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

就像其他注释和答案中指出的那样,没有办法检查变量是否已初始化。 这就是为什么将vars初始化为NULL ,然后进行检查被认为是一种好习惯的原因。

如果您确实要验证函数指针是否指向正确的位置,则可以导出函数并从ELF符号加载指针(请参阅: http : //gcc.gnu.org/wiki/Visibility

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

为了避免这种情况,您可以提供一个“构造函数”,如下所示:

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;
}

然后在文档中写下创建“客户端”的唯一有效方法是使用函数。 如果执行此操作,还应该提供一个destroy函数,在其中调用free

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

始终首先检查空参数。

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

至于您的问题,请在分配完客户结构后使其无效。 这样,您可以确保未分配的函数指针确实为== NULL。

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

    return c;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM