C語言根據函數指針得到其函數名

前言

這次遇到的需求是需要知道函數指針指向的函數的名稱是什麼。感覺是不是有點兒像java裏的反射,可以知道運行時的類是啥。

函數指針介紹

  1. 變量名不需要存儲,是給人看的,可以參考知乎的這個回答。可以簡單理解成編譯器編譯的時候知道每個變量所儲存的地址,在編譯完後會把變量替換成值的存儲地址。那麼,函數也是有存儲地址的。
  2. 既然數據變量的內存地址可以存儲在相應的指針變量中。函數指針中可以存儲函數的首地址,這樣就可以通過函數指針變量來調用其指向的函數了。

函數指針的示例代碼

函數指針的示例代碼,摘自Wikipedia:

# ifndef __cplusplus
    # include <stdio.h>
# else
    # include <cstdio>
# endif

int max(int x, int y)
{
    return x > y ? x : y;
}

int main(void)
{
    /* p 是函數指針 */
    /* 指向具有相同的參數類型,返回類型的函數 */
    int (* p)(int, int) = & max;  // &可以省略
    int a, b, c, d;

    printf("please input 3 numbers:");
    scanf("%d %d %d", & a, & b, & c);

    /* 與直接調用函數等價,d = max(max(a, b), c) */
    d = p(p(a, b), c); 

    printf("the maxumum number is: %d\n", d);

    return 0;
}

代碼中,函數名max是一個函數指針常量,而我們定義的函數指針p則是一個函數指針變量

解決方法

回到我們的問題,該如何根據函數指針得到它指向的函數的名稱呢?

方法一:內核態、printk()函數

如果是在內核態中,那麼可以通過printk()函數來打印出函數名。詳情可以參考下網上的Blog。並且需要包含相關的頭文件:#include<linux/kernel.h>
或者,如果是在用戶態中,那麼需要寫Makefile,將內核態中的模塊編譯鏈接,可以參考下這個鏈接:linux/module.h: No such file or directory。這個回答裏也有人提到用-I加上鍊接庫,但是我沒試成功,手動狗頭。

方法二:func

在要打印的函數中嵌入以下代碼:

printf("%s", __func__);

其中,__func__ 是個標準宏,它能給出當前的函數名。
但是我覺得這樣的話,因爲我的文件中有很多個函數,而我只能對這些函數的代碼進行入侵,添加打印的代碼。可以想想有沒有什麼設計模式可以解決的。

方法三:創建函數指針與函數名對應的字典

創建相關的結構體作爲函數地址函數名的字典,在判斷的時候進行遍歷,看函數指針的地址與函數的地址是否相同,相同的話就可以返回其函數名。
缺點的話就是在創建結構體數組的時候,如果有很多函數的話就得一個一個添加。

示例代碼:

typedef void (*simple_fp)();  // 函數指針
typedef struct _function_meta {
    simple_fp func_ptr;
    char * func_name;
} function_meta;  // 函數元數據的結構體,包含函數指針以及函數指針對應的函數名

void f1() {}
void f2() {}
void f3() {}

int main()
{
    void (*unknown_func_pointer)() = f2; // you ignore this
    unknown_func_pointer(); // this is all you see

    printf("f1 %p\n", f1);
    printf("f2 %p\n", f2);
    printf("f3 %p\n", f3);

    printf("unknown_func_pointer %p\n", unknown_func_pointer);

    struct function_meta arrfunc_ptrs[3] = {
	    										{f1, "f1"}, 
	    										{f2, "f2"}, 
	    										{f3, "f3"}
    										};  // 函數元數據數組

    for(int i=0; i<3; i++) 
    {
        if( unknown_func_pointer == arrfunc_ptrs[i].func_ptr )  // 判斷要判斷的函數指針與函數元數據數組中每個元素的函數指針是否相同
        {
            printf("function name: %s\n", arrfunc_ptrs[i].func_name);  // 是的話則可以打印出其函數名
        }
    }
}

輸出如下:

f1 0x40051b
f2 0x400521
f3 0x400527
unknown_func_pointer 0x400521
function name: f2

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章