簡體   English   中英

如何解釋從 function 指針數組調用 C function?

[英]How to explain calling a C function from an array of function pointers?

此示例https://godbolt.org/z/EKvvEKa6T使用此語法調用 MyFun()

(*((int(**)(void))CallMyFun))();

是否有該混淆語法的 C 細分來解釋它是如何工作的?

#include <stdio.h>

int MyFun(void)
{
    printf("Hello, World!");
    return 0;
}

void *funarray[] = { NULL,NULL,&MyFun,NULL,NULL };

int main(void)
{
    size_t CallMyFun = (size_t)&funarray + (2 * sizeof(funarray[0]));
    return (*((int(**)(void))CallMyFun))();
}

void *funarray[] = { NULL,NULL,&MyFun,NULL,NULL };的行為未由 C 標准定義,因為它將指向 function ( MyFun ) 的指針轉換為指向 object 類型 ( void * ) 的指針。 C 2018 6.3.2.3中規定了指針的轉換,除null指針外,均未涵蓋指向object類型的指針與指向function類型的指針之間的轉換。

代碼size_t CallMyFun = (size_t)&funarray + (2 * sizeof(funarray[0])); CallMyFun設置為funarray的元素 2 的地址,前提是指向 integer size_t類型的指針轉換“自然”地工作,這不是 C 標准所要求的,但有意為之。

(*((int(**)(void))CallMyFun))(); ,我們有(int(**)(void)) CallMyFun 這表示將CallMyFun轉換為指向 function 的指針,不接受 arguments 並返回int 在C中使用異構function指針表時,需要進行類型轉換,因為C沒有提供通用的function指針機制,所以不能怪作者。 但是,這不僅會轉換為指向 function 類型的指針,還會轉換為指向 function 類型的指針,這是一個錯誤。

該數組包含指向void的指針,而不是指向函數指針的指針。 C 標准不要求它們具有相同的大小或表示形式。

然后代碼應用* ,打算檢索指針。 這是另一個錯誤。 如果指向void的指針具有相同的大小並且表示具有指向 function 的指針的指針,則訪問void * (這是存儲在數組中的內容)作為指向 function 的指針(這是使用它檢索它的內容)表達式確實)違反了 C 2018 6.5 7 中的別名規則。

但是,如果 C 實現確實支持這個以及所有先前的轉換和問題,則結果是指向 function MyFun的指針,然后應用()調用 function。

假設數組必須支持異構 function 類型,編寫代碼的正確方法可能是:

// Use array of pointers to functions (of a forced type).
void (*funarray[])(void) = { NULL, NULL, (void (*)(void)) MyFun, NULL, NULL };

…

// Get array element using ordinary subscript notation.
void (*CallMyFunc)(void) = funarray[2];

// Convert pointer to function’s actual type, then call it.
return ((int (*)(void)) CallMyFunc)();

如果數組可以齊次,那么代碼應該這樣寫:

int (*funarray[])(void) = { NULL, NULL, MyFun, NULL, NULL };

…

return funarray[2]();

要了解這里發生了什么,我們應該逐段查看代碼的每個部分(至少是奇數段):

void *funarray[] = { NULL,NULL,&MyFun,NULL,NULL };

在上面,我們正在創建一個初始化為最“空”的指針數組(即NULL ),但在索引 2 處我們有一個指向MyFunc的指針。 跟蹤我們 go 的類型很有幫助,所以此時,我們有類型為int (*)(void)&MyFunc (C 中的函數指針語法有點奇怪,因為*和可能的標識符位於中間而不是位於最后),它立即變成數組中的void * 為什么數組的類型是void *當它看起來包含 function 指針時有點奇怪,但讓我們假設有一個很好的理由......

size_t CallMyFun = (size_t)&funarray + (2 * sizeof(funarray[0]));

這段代碼是一種相當復雜的訪問數組和獲取第二個元素的方法,方法是將數組的頭部轉換為size_t ,然后添加正確的字節數以獲得第二個條目的地址(更簡單的方法是(size_t) &funarray[2] )。 在這里,我們再次丟失了類型信息,從最初的int (*[])(void)void*[]size_t

return (*((int(**)(void))CallMyFun))();

現在我們知道CallMyFunc包含一個指向 function 指針的指針,即數組中MyFunc指針的地址,如果我們想調用它,我們需要將它轉換回正確的類型並正確地解引用它。 因此,展開調用,我們直接有((int(**)(void))CallMyFun)將類型不正確的CallMyFuncsize_t轉換為雙精度 function 指針,即指向 function 指針的指針,其語法為int (**)(void) ,就像任何其他需要兩個 * 來表示的雙指針一樣。 接下來,由於還不是 function 指針,我們需要取消引用它以獲得類型為int (*)(void)的指針,因此(*((int(**)(void))CallMyFun)) 最后,我們要實際調用 function 指針,所以最后一組父對象。

正如評論中所提到的,這段代碼通過改變類型和使用不尋常的方式訪問 arrays 而變得相當混亂; 通常最好保持類型一致,因為它可以讓編譯器幫助您避免錯誤,並使代碼對您自己和其他人更具可讀性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM