简体   繁体   English

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

[英]Check if a pointer to function is initialized

How can I check if a pointer to function was initialized? 如何检查指向函数的指针是否已初始化? I can check for NULL, but if not null could be garbage, right? 我可以检查NULL,但如果不为null则可能是垃圾,对吗?

I have the following: 我有以下几点:

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

How can I check if this pointer really points to function "void (*f)(client *)"? 如何检查该指针是否真正指向函数“ void(* f)(client *)”? Comparing size doesn't work because could garbage in same size, correct? 比较大小不起作用,因为可以将相同大小的垃圾正确吗? I would like a way to accomplish that preferably according to C standard. 我想要一种最好根据C标准来实现的方法。

Caveats 注意事项

Checking if a pointer to a function is initialized with an valid function is not an easily solvable problem. 检查指向函数的指针是否已使用有效函数初始化不是一个容易解决的问题。 Any solution, will not be portable across platforms, and is also dependent on the binary format (statically or dynamically linkable formats) that you end up with. 任何解决方案都不能跨平台移植,并且还取决于最终使用的二进制格式(静态或动态可链接格式)。 There are ways to do this, with varying success, on different binary formats, however I am not going to go over every permutation. 有多种方法可以在不同的二进制格式上取得不同的成功,但是我不会遍历所有排列。 Hopefully this will get you going down that rabbit hole :-) and you can figure out the particular solution that works for you in your circumstances. 希望这会让您步入困境:-),然后您可以找出适合您情况的特定解决方案。

In order for some of the solutions to work you have to ensure that the linked binaries have exported symbols (it's possible to do it without, but it's a lot harder and I don't have the time). 为了使某些解决方案起作用,您必须确保链接的二进制文件已导出符号(可以不使用它而做,但是这样做要困难得多,而且我没有时间)。 So when you're linking your program ensure that you have dynamic symbols enabled. 因此,在链接程序时,请确保已启用动态符号。

Having said that, here's an approach you can use on systems using dlfcn functions. 话虽如此,这是可以在使用dlfcn函数的系统上使用的一种方法。 (See History below) (请参见下面的历史记录)

More Caveats 更多警告

As @Deduplicator points out in his comment below, there may be situations where 0xdeadbeef may arbitrarily happen to point to a valid function, in which case you may end up with a situation where you end up calling the wrong valid function. 就像@Deduplicator在下面的评论中指出的那样,在某些情况下,可能会任意发生0xdeadbeef指向有效函数的情况,在这种情况下,您可能最终会遇到调用错误的有效函数的情况。 There are ways to mitigate that situation at either compile-time or runtime but you'll have to build the solution by hand. 有一些方法可以在编译时或运行时减轻这种情况,但是您必须手动构建解决方案。 For example, C++ does it by mangling in namespace into the symbols. 例如,C ++通过将名称空间改编为符号来实现。 You could require that to happen. 您可能要求做到这一点。 (I'll think of an interesting way to do this and post it) (我会想到一种有趣的方法来完成并发布)

Linux / SysV variants (Mac OSX included) Linux / SysV变体(包括Mac OSX)

Use dladdr (SysV) (GNU has a dladdr1 as well) to determine which function does the address you provide fall within: 使用dladdr (SysV)(GNU也有dladdr1)来确定您提供的地址属于哪个函数

Example: 例:

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

NOTE Enable dynamic symbols for this to work 注意启用动态符号以使其起作用

GCC/LLVM etc. GCC / LLVM等

use -rdynamic or -Wl,--export-dynamic during the link process, so compile with: 在链接过程中使用-rdynamic-Wl,--export-dynamic -rdynamic -Wl,--export-dynamic ,因此请使用以下命令进行编译:

gcc -o ex1 -rdynamic ex1.c

Windows 视窗

Windows does its own thing (as always) and I haven't tested any of these, but the basic concept should work: Windows做自己的事情(一如既往),并且我还没有测试过其中任何一个,但是基本概念应该起作用:

Use GetModuleHandle and EnumCurrentProcess together to get loaded symbol information and run through the pointers in a loop to see they match any of the address therein. 一起使用GetModuleHandleEnumCurrentProcess可获得加载的符号信息,并在循环中遍历指针以查看它们与其中的任何地址匹配。

The other way would be to use VirtualQuery and then cast mbi.AllocationBase to (HMODULE) and see if you get the path of your own binary back. 另一种方法是使用VirtualQuery ,然后将mbi.AllocationBase为(HMODULE),以查看是否获得了自己的二进制文件的路径。

In c function pointers are no different than regular pointers and by standard they have one value that says the value should not be used and this is NULL . 在c函数中,指针与常规指针没有什么不同,按照标准,它们有一个值表示不应使用该值,并且该值为NULL

The way you should work with pointers is to set them only to valid value or NULL . 应该使用指针的方式是将它们仅设置为有效值或NULL There is no other way you can be sure there is a OK value. 没有其他方法可以确保确定值。 And by definition every value that is not NULL should be considered valid. 根据定义,每个非NULL都应视为有效。

Like pointed to in other comments and answers, there is not way to check a variable is initialized. 就像其他注释和答案中指出的那样,没有办法检查变量是否已初始化。 That's why initializing vars to NULL and then checking is considered good practice. 这就是为什么将vars初始化为NULL ,然后进行检查被认为是一种好习惯的原因。

If you really want to validate your function pointer is pointing to the correct place, you could export the function and load your pointer from the ELF symbols (see: http://gcc.gnu.org/wiki/Visibility ) 如果您确实要验证函数指针是否指向正确的位置,则可以导出函数并从ELF符号加载指针(请参阅: http : //gcc.gnu.org/wiki/Visibility

As described in the comments, it is impossible to determine with 100% certainty whether a pointer is garbage. 如评论中所述,不可能100%地确定指针是否为垃圾。

To avoid such situation, you can provide a "constructor" function, like this: 为了避免这种情况,您可以提供一个“构造函数”,如下所示:

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

Then write in your documentation that the only valid way to create "clients" is by using your function. 然后在文档中写下创建“客户端”的唯一有效方法是使用函数。 If you do this, you should also provide a destroy function, where you call free . 如果执行此操作,还应该提供一个destroy函数,在其中调用free

Suppose you add a new pointer to your struct one day. 假设您有一天要向struct添加新的指针。 Then you update your client_allocate function, where you set this pointer to NULL , and the new pointer will always be properly initialized. 然后,更新client_allocate函数,将该指针设置为NULL ,新指针将始终正确初始化。 There is no need to update all places in code where your struct is allocated, because now there is only one such place. 无需更新代码中分配struct所有位置,因为现在只有一个这样的位置。

Always check for null parameters first of all. 始终首先检查空参数。

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

As for your question, nullify your client struct after it's been malloc'd. 至于您的问题,请在分配完客户结构后使其无效。 This way you can ensure that an unassigned function pointer shall indeed ==NULL. 这样,您可以确保未分配的函数指针确实为== 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