簡體   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