Pointer fungsi adalah variabel yang berisi alamat suatu fungsi. Karena itu adalah variabel pointer meskipun dengan beberapa properti terbatas, Anda dapat menggunakannya cukup banyak seperti Anda akan variabel pointer lainnya dalam struktur data.
Satu-satunya pengecualian yang dapat saya pikirkan adalah memperlakukan pointer fungsi sebagai menunjuk ke sesuatu selain nilai tunggal. Melakukan aritmatika pointer dengan menambah atau mengurangi pointer fungsi atau menambah / mengurangi offset ke pointer fungsi tidak benar-benar bermanfaat karena pointer fungsi hanya menunjuk ke satu hal, titik masuk dari suatu fungsi.
Ukuran variabel pointer fungsi, jumlah byte yang ditempati oleh variabel, dapat bervariasi tergantung pada arsitektur yang mendasarinya, misalnya x32 atau x64 atau apa pun.
Deklarasi untuk variabel penunjuk fungsi perlu menentukan jenis informasi yang sama dengan deklarasi fungsi agar kompiler C melakukan pengecekan seperti biasanya. Jika Anda tidak menentukan daftar parameter dalam deklarasi / definisi pointer fungsi, kompiler C tidak akan dapat memeriksa penggunaan parameter. Ada kasus-kasus ketika kurangnya pemeriksaan ini dapat bermanfaat namun ingatlah bahwa jaring pengaman telah dilepas.
Beberapa contoh:
int func (int a, char *pStr); // declares a function
int (*pFunc)(int a, char *pStr); // declares or defines a function pointer
int (*pFunc2) (); // declares or defines a function pointer, no parameter list specified.
int (*pFunc3) (void); // declares or defines a function pointer, no arguments.
Dua deklarasi pertama agak mirip yaitu:
func
adalah fungsi yang mengambil int
dan char *
dan mengembalikanint
pFunc
adalah fungsi pointer yang ditugaskan alamat sebuah fungsi yang mengambil int
dan char *
dan kembali sebuahint
Jadi dari atas kita dapat memiliki baris sumber di mana alamat fungsi func()
ditugaskan ke variabel pointer fungsi pFunc
seperti pada pFunc = func;
.
Perhatikan sintaks yang digunakan dengan deklarasi / definisi pointer fungsi di mana tanda kurung digunakan untuk mengatasi aturan prioritas operator alami.
int *pfunc(int a, char *pStr); // declares a function that returns int pointer
int (*pFunc)(int a, char *pStr); // declares a function pointer that returns an int
Beberapa Contoh Penggunaan Yang Berbeda
Beberapa contoh penggunaan pointer fungsi:
int (*pFunc) (int a, char *pStr); // declare a simple function pointer variable
int (*pFunc[55])(int a, char *pStr); // declare an array of 55 function pointers
int (**pFunc)(int a, char *pStr); // declare a pointer to a function pointer variable
struct { // declare a struct that contains a function pointer
int x22;
int (*pFunc)(int a, char *pStr);
} thing = {0, func}; // assign values to the struct variable
char * xF (int x, int (*p)(int a, char *pStr)); // declare a function that has a function pointer as an argument
char * (*pxF) (int x, int (*p)(int a, char *pStr)); // declare a function pointer that points to a function that has a function pointer as an argument
Anda dapat menggunakan daftar parameter panjang variabel dalam definisi pointer fungsi.
int sum (int a, int b, ...);
int (*psum)(int a, int b, ...);
Atau Anda tidak dapat menentukan daftar parameter sama sekali. Ini bisa bermanfaat tetapi menghilangkan peluang bagi kompiler C untuk melakukan pemeriksaan pada daftar argumen yang disediakan.
int sum (); // nothing specified in the argument list so could be anything or nothing
int (*psum)();
int sum2(void); // void specified in the argument list so no parameters when calling this function
int (*psum2)(void);
Pemain gaya C
Anda dapat menggunakan gips gaya C dengan pointer fungsi. Namun perlu diketahui bahwa kompiler C mungkin longgar tentang pemeriksaan atau memberikan peringatan daripada kesalahan.
int sum (int a, char *b);
int (*psplsum) (int a, int b);
psplsum = sum; // generates a compiler warning
psplsum = (int (*)(int a, int b)) sum; // no compiler warning, cast to function pointer
psplsum = (int *(int a, int b)) sum; // compiler error of bad cast generated, parenthesis are required.
Bandingkan Function Pointer dengan Equality
Anda dapat memeriksa bahwa pointer fungsi sama dengan alamat fungsi tertentu menggunakan if
pernyataan meskipun saya tidak yakin seberapa berguna itu. Operator pembanding lainnya tampaknya memiliki utilitas yang lebih sedikit.
static int func1(int a, int b) {
return a + b;
}
static int func2(int a, int b, char *c) {
return c[0] + a + b;
}
static int func3(int a, int b, char *x) {
return a + b;
}
static char *func4(int a, int b, char *c, int (*p)())
{
if (p == func1) {
p(a, b);
}
else if (p == func2) {
p(a, b, c); // warning C4047: '==': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
} else if (p == func3) {
p(a, b, c);
}
return c;
}
Array Pointer Fungsi
Dan jika Anda ingin memiliki array fungsi pointer masing-masing elemen di mana daftar argumen memiliki perbedaan maka Anda dapat mendefinisikan pointer fungsi dengan daftar argumen tidak ditentukan (bukan void
berarti tidak ada argumen tetapi hanya tidak ditentukan) sesuatu seperti berikut ini meskipun Anda mungkin melihat peringatan dari kompiler C. Ini juga berfungsi untuk parameter penunjuk fungsi ke fungsi:
int(*p[])() = { // an array of function pointers
func1, func2, func3
};
int(**pp)(); // a pointer to a function pointer
p[0](a, b);
p[1](a, b, 0);
p[2](a, b); // oops, left off the last argument but it compiles anyway.
func4(a, b, 0, func1);
func4(a, b, 0, func2); // warning C4047: 'function': 'int (__cdecl *)()' differs in levels of indirection from 'char *(__cdecl *)(int,int,char *)'
func4(a, b, 0, func3);
// iterate over the array elements using an array index
for (i = 0; i < sizeof(p) / sizeof(p[0]); i++) {
func4(a, b, 0, p[i]);
}
// iterate over the array elements using a pointer
for (pp = p; pp < p + sizeof(p)/sizeof(p[0]); pp++) {
(*pp)(a, b, 0); // pointer to a function pointer so must dereference it.
func4(a, b, 0, *pp); // pointer to a function pointer so must dereference it.
}
Gaya C namespace
Menggunakan Global struct
dengan Function Pointer
Anda dapat menggunakan static
kata kunci untuk menentukan fungsi yang namanya lingkup file dan kemudian menetapkan ini ke variabel global sebagai cara menyediakan sesuatu yang mirip dengan namespace
fungsi C ++.
Dalam file header, tentukan struct yang akan menjadi namespace kita bersama dengan variabel global yang menggunakannya.
typedef struct {
int (*func1) (int a, int b); // pointer to function that returns an int
char *(*func2) (int a, int b, char *c); // pointer to function that returns a pointer
} FuncThings;
extern const FuncThings FuncThingsGlobal;
Kemudian di file sumber C:
#include "header.h"
// the function names used with these static functions do not need to be the
// same as the struct member names. It's just helpful if they are when trying
// to search for them.
// the static keyword ensures these names are file scope only and not visible
// outside of the file.
static int func1 (int a, int b)
{
return a + b;
}
static char *func2 (int a, int b, char *c)
{
c[0] = a % 100; c[1] = b % 50;
return c;
}
const FuncThings FuncThingsGlobal = {func1, func2};
Ini kemudian akan digunakan dengan menentukan nama lengkap variabel struct global dan nama anggota untuk mengakses fungsi. The const
pengubah digunakan pada begitu global yang tidak dapat diubah oleh kecelakaan.
int abcd = FuncThingsGlobal.func1 (a, b);
Area Aplikasi Pointer Fungsi
Komponen pustaka DLL dapat melakukan sesuatu yang mirip dengan namespace
pendekatan gaya C di mana antarmuka pustaka tertentu diminta dari metode pabrik di antarmuka pustaka yang mendukung pembuatan struct
pointer fungsi yang mengandung .. Antarmuka pustaka ini memuat versi DLL yang diminta, membuat struct dengan pointer fungsi yang diperlukan, dan kemudian mengembalikan struct ke pemanggil yang meminta untuk digunakan.
typedef struct {
HMODULE hModule;
int (*Func1)();
int (*Func2)();
int(*Func3)(int a, int b);
} LibraryFuncStruct;
int LoadLibraryFunc LPCTSTR dllFileName, LibraryFuncStruct *pStruct)
{
int retStatus = 0; // default is an error detected
pStruct->hModule = LoadLibrary (dllFileName);
if (pStruct->hModule) {
pStruct->Func1 = (int (*)()) GetProcAddress (pStruct->hModule, "Func1");
pStruct->Func2 = (int (*)()) GetProcAddress (pStruct->hModule, "Func2");
pStruct->Func3 = (int (*)(int a, int b)) GetProcAddress(pStruct->hModule, "Func3");
retStatus = 1;
}
return retStatus;
}
void FreeLibraryFunc (LibraryFuncStruct *pStruct)
{
if (pStruct->hModule) FreeLibrary (pStruct->hModule);
pStruct->hModule = 0;
}
dan ini dapat digunakan seperti pada:
LibraryFuncStruct myLib = {0};
LoadLibraryFunc (L"library.dll", &myLib);
// ....
myLib.Func1();
// ....
FreeLibraryFunc (&myLib);
Pendekatan yang sama dapat digunakan untuk mendefinisikan lapisan perangkat keras abstrak untuk kode yang menggunakan model tertentu dari perangkat keras yang mendasarinya. Pointer fungsi diisi dengan fungsi khusus perangkat keras oleh pabrik untuk menyediakan fungsionalitas khusus perangkat keras yang mengimplementasikan fungsi yang ditentukan dalam model perangkat keras abstrak. Ini dapat digunakan untuk menyediakan lapisan perangkat keras abstrak yang digunakan oleh perangkat lunak yang memanggil fungsi pabrik untuk mendapatkan antarmuka fungsi perangkat keras tertentu kemudian menggunakan pointer fungsi yang disediakan untuk melakukan tindakan untuk perangkat keras yang mendasarinya tanpa perlu mengetahui detail implementasi tentang target spesifik .
Pointer Fungsi untuk membuat Delegasi, Penangan, dan Callback
Anda dapat menggunakan pointer fungsi sebagai cara untuk mendelegasikan beberapa tugas atau fungsi. Contoh klasik dalam C adalah pointer fungsi delegasi perbandingan yang digunakan dengan fungsi pustaka C Standar qsort()
dan bsearch()
untuk memberikan urutan pemeriksaan untuk menyortir daftar item atau melakukan pencarian biner pada daftar item yang diurutkan. Delegasi fungsi perbandingan menentukan algoritma pemeriksaan yang digunakan dalam pengurutan atau pencarian biner.
Penggunaan lain mirip dengan menerapkan algoritma ke wadah Perpustakaan Template C ++ Standar.
void * ApplyAlgorithm (void *pArray, size_t sizeItem, size_t nItems, int (*p)(void *)) {
unsigned char *pList = pArray;
unsigned char *pListEnd = pList + nItems * sizeItem;
for ( ; pList < pListEnd; pList += sizeItem) {
p (pList);
}
return pArray;
}
int pIncrement(int *pI) {
(*pI)++;
return 1;
}
void * ApplyFold(void *pArray, size_t sizeItem, size_t nItems, void * pResult, int(*p)(void *, void *)) {
unsigned char *pList = pArray;
unsigned char *pListEnd = pList + nItems * sizeItem;
for (; pList < pListEnd; pList += sizeItem) {
p(pList, pResult);
}
return pArray;
}
int pSummation(int *pI, int *pSum) {
(*pSum) += *pI;
return 1;
}
// source code and then lets use our function.
int intList[30] = { 0 }, iSum = 0;
ApplyAlgorithm(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), pIncrement);
ApplyFold(intList, sizeof(int), sizeof(intList) / sizeof(intList[0]), &iSum, pSummation);
Contoh lain adalah dengan kode sumber GUI di mana handler untuk acara tertentu terdaftar dengan menyediakan pointer fungsi yang sebenarnya dipanggil ketika peristiwa itu terjadi. Kerangka kerja Microsoft MFC dengan peta pesannya menggunakan sesuatu yang mirip untuk menangani pesan Windows yang dikirim ke jendela atau utas.
Fungsi asinkron yang memerlukan panggilan balik mirip dengan pengendali event. Pengguna fungsi asinkron memanggil fungsi asinkron untuk memulai beberapa tindakan dan menyediakan penunjuk fungsi yang akan dipanggil fungsi asinkron setelah aksi selesai. Dalam hal ini acara adalah fungsi asinkron yang menyelesaikan tugasnya.