1. Указатель функции
Указатель функции используется для указания на функцию
Имя функции — это адрес входа в тело функции.
1) Указатели функций могут быть определены типами функций: указатель FuncType*;
2) Его также можно определить напрямую: type (*pointer)(список параметров);
указатель - это имя переменной указателя функции
type — тип возвращаемого значения указателя на функцию
список параметров — это список типов параметров, указывающих на функцию
пример:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int Add(int a, int b)
{
int c = 0;
c = a + b;
return c;
}
int main()
{
// вызов напрямую
int sum = Add(1, 2);//Имя функции представляет собой адрес входа функции, который является указателем на функцию ADD
coutendl;
// Объявить тип функции для косвенного вызова.
{
typedef int(MyFuncType)(int a, int b);
MyFuncType *myFuncVar = NULL;
myFuncVar = Add;
int sum1=myFuncVar(2, 4);
coutendl;
}
// Объявить функцию для ссылки на тип.
{
Typedef int(*PFuncType)(int a, int b);//Объявить указатель на функцию Компилятор C не будет выделять память
PFuncType myFunc = NULL;
myFunc = &Add;
int sum2 = myFunc(3, 5);
coutendl;
}
//Определяем указатель на функцию, чтобы нужен был только адрес входа функции
{
int(*MYPFUNC)(int a, int b);
MYPFUNC = Add;
int sum3=MYPFUNC(10, 5);
coutendl;
}
return 0;
}
2. Указатели функций как параметры функций
Указатель функции как параметр функции: когда указатель функции используется в качестве параметра функции и передается вызываемой функции, вызываемая функция может вызвать внешнюю функцию через этот указатель, который формирует обратный вызов.
пример:
#include <windows.h>
#include <stdio.h>
#include <iostream>
using namespace std;
int Add(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add Done!\n");
return c;
}
int Add2(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add2 Done!\n");
return c;
}
int Add3(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add3 Done!\n");
return c;
}
int Add4(int a, int b)
{
int c = 0;
c = a + b;
printf("FUNC Add4 Done!\n");
return c;
}
int myMainFunc(int(*myFuncAdd)(int a, int b))
{
int sum= myFuncAdd(10, 20);
return sum;
}
int main()
{
myMainFunc(Add);
myMainFunc(Add2);
myMainFunc(Add3);
myMainFunc(Add4);
return 0;
}
результат операции:
Анализ идей:
1. Использование функции указателя эффективно отделяет вызов функции от реализации функции и реализует развязку.
2. int myMainFunc(int(*myFuncAdd)(int a, int b)) — это функция в библиотеке, используется только обратный вызов, а адрес внешней функции передается через параметр указателя функции для реализации вызова
3. Изменен код функции Add, и код библиотеки можно нормально вызывать без изменения кода библиотеки, что удобно для сопровождения и обновления программы.
3. Указатели функций используются в качестве параметров функций в реальных проектах.
Первый прямой вызов
пример:
Есть socketclient.dll, и метод dll можно посмотреть с помощью зависимостей walker.
Первый шаг Загрузить DLL
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCSTR sss = pathStr.c_str();
hInstance =LoadLibrary("socketclient.dll");
if (hInstance == NULL)
{
printf("Загрузить библиотеку() не удалось");
return 0;
}
Обратите внимание на проблему с кодировкой здесь.
Свойства правой кнопки мыши следующие:
Измените набор символов, чтобы использовать многобайтовый набор символов.
Если используется набор символов Unicode, его необходимо изменить.В Unicode параметром LoadLibrary является LPCWSTR. Поэтому его нужно преобразовать.
LPCWSTR stringToLPCWSTR(string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t)*(orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
int main()
{
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCTSTR sss = stringToLPCWSTR(pathStr);
hInstance =LoadLibrary(sss);
if (hInstance == NULL)
{
printf("Загрузить библиотеку() не удалось");
return 0;
}
}
Второй шаг — объявить тип указателя функции.
// Инициализация клиента получает дескриптор вверх и вниз
//объявляем тип указателя на функцию
typedef int(*CltSocketInit)(void **handle /*out*/);
// клиент отправляет сообщение
typedef int(*CltSocketSend)(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/);
// клиент получает сообщение
typedef int(*CltSocketRev)(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);
// Клиент освобождает ресурсы
typedef int(*CltSocketDestory)(void *handle/*in*/);
Третий шаг — реализация вызова функции.
CltSocketInit cltSocketInit = (CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit");
if (cltSocketInit == NULL)
{
return 0;
}
CltSocketSend cltSocketSend = (CltSocketSend)::GetProcAddress(hInstance, "cltSocketSend");
CltSocketRev cltSocketRev = (CltSocketRev)::GetProcAddress(hInstance, "cltSocketRev");
CltSocketDestory cltSocketDestory = (CltSocketDestory)::GetProcAddress(hInstance, "cltSocketDestory");
//Выполнить вызов функции динамической библиотеки
unsigned char buf[128];
int buflen = 128;
unsigned char outbuf[4096];
int outbuflen = 4096;
strcpy((char *)buf, "aaaaaaaaaafffffffffdddddd");
buflen = 9;
void *handle = NULL;
int ret = 0;
ret = cltSocketInit(&handle);
ret = cltSocketSend(handle, buf, buflen);
ret = cltSocketRev(handle, outbuf, &outbuflen);
ret = cltSocketDestory(handle);
if (memcmp(buf, outbuf, outbuflen) == 0)
{
printf("Отправленные данные совпадают с полученными данными ok\n");
}
else
{
printf("Отправленные данные отличаются от полученных\n");
}
Полный код выглядит следующим образом:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <iostream>
using namespace std;
// Инициализация клиента получает дескриптор вверх и вниз
//объявляем тип указателя на функцию
typedef int(*CltSocketInit)(void **handle /*out*/);
// клиент отправляет сообщение
typedef int(*CltSocketSend)(void *handle /*in*/, unsigned char *buf /*in*/, int buflen /*in*/);
// клиент получает сообщение
typedef int(*CltSocketRev)(void *handle /*in*/, unsigned char *buf /*in*/, int *buflen /*in out*/);
// Клиент освобождает ресурсы
typedef int(*CltSocketDestory)(void *handle/*in*/);
LPCWSTR stringToLPCWSTR(string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t)*(orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
int main()
{
HINSTANCE hInstance;
string pathStr= "socketclient.dll";
LPCTSTR sss = stringToLPCWSTR(pathStr);
hInstance =LoadLibrary(sss);
if (hInstance == NULL)
{
printf("Загрузить библиотеку() не удалось");
return 0;
}
CltSocketInit cltSocketInit = (CltSocketInit)::GetProcAddress(hInstance, "cltSocketInit");
if (cltSocketInit == NULL)
{
return 0;
}
CltSocketSend cltSocketSend = (CltSocketSend)::GetProcAddress(hInstance, "cltSocketSend");
CltSocketRev cltSocketRev = (CltSocketRev)::GetProcAddress(hInstance, "cltSocketRev");
CltSocketDestory cltSocketDestory = (CltSocketDestory)::GetProcAddress(hInstance, "cltSocketDestory");
//Выполнить вызов функции динамической библиотеки
unsigned char buf[128];
int buflen = 128;
unsigned char outbuf[4096];
int outbuflen = 4096;
strcpy((char *)buf, "aaaaaaaaaafffffffffdddddd");
buflen = 9;
void *handle = NULL;
int ret = 0;
ret = cltSocketInit(&handle);
ret = cltSocketSend(handle, buf, buflen);
ret = cltSocketRev(handle, outbuf, &outbuflen);
ret = cltSocketDestory(handle);
if (memcmp(buf, outbuf, outbuflen) == 0)
{
printf("Отправленные данные совпадают с полученными данными ok\n");
}
else
{
printf("Отправленные данные отличаются от полученных\n");
}
return 0;
}
результат операции: