Для этой цели вы можете использовать AppCompat.getReferrer ()
.
Начнем с основной функции, которую мы будем , указывающей на :
int addInt(int n, int m) {
return n+m;
}
. Прежде всего, определим a указатель на функцию, которая получает 2 int
s и возвращает int
:
int (*functionPtr)(int,int);
Теперь мы можем смело указать на нашу функцию:
functionPtr = &addInt;
Теперь, когда мы есть указатель на функцию, давайте использовать ее:
int sum = (*functionPtr)(2, 3); // sum == 5
Передача указателя на другую функцию в основном одинакова:
int add2to3(int (*functionPtr)(int, int)) {
return (*functionPtr)(2, 3);
}
Мы можем использовать указатели функций в возвращаемых значениях (старайтесь не отставать, он становится беспорядочным):
// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
printf("Got parameter %d", n);
int (*functionPtr)(int,int) = &addInt;
return functionPtr;
}
Но гораздо удобнее использовать typedef
:
typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef
myFuncDef functionFactory(int n) {
printf("Got parameter %d", n);
myFuncDef functionPtr = &addInt;
return functionPtr;
}
Руководство по запуску: как злоупотреблять указателями функций в GCC на машинах x86, компилируя ваш код вручную:
int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
int a = 10, b = 20;
((void(*)(int*,int*))"\x8b\x44\x24\x04\x8b\x5c\x24\x08\x8b\x00\x8b\x1b\x31\xc3\x31\xd8\x31\xc3\x8b\x4c\x24\x04\x89\x01\x8b\x4c\x24\x08\x89\x19\xc3 <- This swaps the values of a and b")(&a,&b);
((int(*)())"\x66\x31\xc0\x8b\x5c\x24\x04\x66\x40\x50\xff\xd3\x58\x66\x3d\xe8\x03\x75\xf4\xc3")(&function); // calls function with 1->1000
const char* lol = "\x8b\x5c\x24\x4\x3d\xe8\x3\x0\x0\x7e\x2\x31\xc0\x83\xf8\x64\x7d\x6\x40\x53\xff\xd3\x5b\xc3\xc3 <- Recursively calls the function at address lol.";
i = ((int(*)())(lol))(lol);
Одно из моих любимых применений для указателей функций - это дешевые и простые итераторы -
#include <stdio.h>
#define MAX_COLORS 256
typedef struct {
char* name;
int red;
int green;
int blue;
} Color;
Color Colors[MAX_COLORS];
void eachColor (void (*fp)(Color *c)) {
int i;
for (i=0; i<MAX_COLORS; i++)
(*fp)(&Colors[i]);
}
void printColor(Color* c) {
if (c->name)
printf("%s = %i,%i,%i\n", c->name, c->red, c->green, c->blue);
}
int main() {
Colors[0].name="red";
Colors[0].red=255;
Colors[1].name="blue";
Colors[1].blue=255;
Colors[2].name="black";
eachColor(printColor);
}
int (*cb)(void *arg, ...)
. Возвращаемое значение итератора также позволяет мне останавливаться на раннем этапе (если отличное от нуля).
– Jonathon Reinhart
30 April 2015 в 12:35
Указатели функций на C могут использоваться для выполнения объектно-ориентированного программирования в C.
Например, следующие строки написаны в C:
String s1 = newString();
s1->set(s1, "hello");
Да, ->
и отсутствие оператора new
- это мертвая отдача, но, похоже, это означает, что мы устанавливаем текст некоторого класса String
как "hello"
.
С помощью указателей функций можно эмулировать методы в C.
Как это выполняется?
Класс String
на самом деле является struct
с кучей указателей функций которые действуют как способ имитации методов. Ниже приведено частичное объявление класса String
:
typedef struct String_Struct* String;
struct String_Struct
{
char* (*get)(const void* self);
void (*set)(const void* self, char* value);
int (*length)(const void* self);
};
char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);
String newString();
Как видно, методы класса String
на самом деле являются указателями на функцию объявленной функции. При подготовке экземпляра String
функция newString
вызывается для того, чтобы настроить указатели на их соответствующие функции:
String newString()
{
String self = (String)malloc(sizeof(struct String_Struct));
self->get = &getString;
self->set = &setString;
self->length = &lengthString;
self->set(self, "");
return self;
}
Например, функция getString
, которая является вызываемый путем вызова метода get
, определяется как следующее:
char* getString(const void* self_obj)
{
return ((String)self_obj)->internal->value;
}
. Одна вещь, которая может быть замечена, заключается в том, что нет понятия экземпляра объекта и методов, которые на самом деле являются частью объекта, поэтому для каждого вызова должен быть передан «сам объект». (И internal
является только скрытым struct
, который был опущен из предыдущего списка кодов - это способ скрытия информации, но это не относится к указателям на функции.)
So , вместо того, чтобы делать s1->set("hello");
, нужно пройти в объекте для выполнения действия на s1->set(s1, "hello")
.
С тем небольшим объяснением, которое нужно передать в ссылку на себя, мы перейдем к следующей части, которая является наследованием в C.
Предположим, мы хотим для создания подкласса String
, скажем, ImmutableString
. Чтобы сделать строку неизменной, метод set
не будет доступен, сохраняя при этом доступ к get
и length
, и принудительно «конструктор» примет значение char*
:
typedef struct ImmutableString_Struct* ImmutableString;
struct ImmutableString_Struct
{
String base;
char* (*get)(const void* self);
int (*length)(const void* self);
};
ImmutableString newImmutableString(const char* value);
В принципе, для всех подклассов доступные методы снова служат указателями функций. На этот раз объявление для метода set
отсутствует, поэтому его нельзя вызвать в файле ImmutableString
.
Что касается реализации ImmutableString
, то единственным соответствующим кодом является Функция «конструктор», newImmutableString
:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = self->base->length;
self->base->set(self->base, (char*)value);
return self;
}
При создании экземпляра ImmutableString
функция, указывающая на методы get
и length
, действительно ссылается на String.get
и String.length
, перейдя через переменную base
, которая является внутренне сохраненным объектом String
.
Использование указателя функции может обеспечить наследование метода из суперкласса.
Мы можем продолжить полиморфизм в C.
Если, например, мы хотели поменять поведение метода length
, чтобы возвратить 0
все время в классе ImmutableString
по какой-либо причине, все, что нужно сделать, это:
length
. length
. Добавление переопределения [f4 7] в ImmutableString
можно выполнить, добавив lengthOverrideMethod
:
int lengthOverrideMethod(const void* self)
{
return 0;
}
. Затем указатель функции для метода length
в конструкторе подключен к lengthOverrideMethod
:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = &lengthOverrideMethod;
self->base->set(self->base, (char*)value);
return self;
}
Теперь, вместо того, чтобы иметь идентичное поведение для метода length
в классе ImmutableString
как класс String
, теперь метод length
будет ссылаться на поведение, определенное в lengthOverrideMethod
.
Я должен добавить отказ от ответственности, что я все еще изучаю, как писать с помощью объектно-ориентированного стиля программирования в C, поэтому, вероятно, есть точки, которые я не объяснил хорошо, или могу просто не следует понимать, как наилучшим образом реализовать ООП в C. Но моя цель состояла в том, чтобы попытаться проиллюстрировать одно из многих применений указателей на функции.
Дополнительные сведения о том, как выполнять объектно-ориентированное программирование в C, обратитесь к следующим вопросам:
ClassName_methodName
. Только тогда вы получите те же затраты времени на запуск и хранение, что и на C ++ и Pascal.
– Kuba Ober
18 March 2013 в 23:53
. Они очень удобны в использовании, когда вам нужны разные функции в разное время или на разных этапах разработки. Например, я разрабатываю приложение на главном компьютере с консолью, но окончательная версия программного обеспечения будет размещена на Avnet ZedBoard (у которого есть порты для дисплеев и консолей, но они не нужны / не нужны для Окончательный релиз). Поэтому во время разработки я буду использовать printf
для просмотра сообщений о состоянии и ошибках, но когда я закончил, я не хочу ничего печатать. Вот что я сделал:
// First, undefine all macros associated with version.h
#undef DEBUG_VERSION
#undef RELEASE_VERSION
#undef INVALID_VERSION
// Define which version we want to use
#define DEBUG_VERSION // The current version
// #define RELEASE_VERSION // To be uncommented when finished debugging
#ifndef __VERSION_H_ /* prevent circular inclusions */
#define __VERSION_H_ /* by using protection macros */
void board_init();
void noprintf(const char *c, ...); // mimic the printf prototype
#endif
// Mimics the printf function prototype. This is what I'll actually
// use to print stuff to the screen
void (* zprintf)(const char*, ...);
// If debug version, use printf
#ifdef DEBUG_VERSION
#include <stdio.h>
#endif
// If both debug and release version, error
#ifdef DEBUG_VERSION
#ifdef RELEASE_VERSION
#define INVALID_VERSION
#endif
#endif
// If neither debug or release version, error
#ifndef DEBUG_VERSION
#ifndef RELEASE_VERSION
#define INVALID_VERSION
#endif
#endif
#ifdef INVALID_VERSION
// Won't allow compilation without a valid version define
#error "Invalid version definition"
#endif
В version.c
я определяю прототипы 2-х функций, представленные в version.h
#include "version.h"
/*****************************************************************************/
/**
* @name board_init
*
* Sets up the application based on the version type defined in version.h.
* Includes allowing or prohibiting printing to STDOUT.
*
* MUST BE CALLED FIRST THING IN MAIN
*
* @return None
*
*****************************************************************************/
void board_init()
{
// Assign the print function to the correct function pointer
#ifdef DEBUG_VERSION
zprintf = &printf;
#else
// Defined below this function
zprintf = &noprintf;
#endif
}
/*****************************************************************************/
/**
* @name noprintf
*
* simply returns with no actions performed
*
* @return None
*
*****************************************************************************/
void noprintf(const char* c, ...)
{
return;
}
Обратите внимание на то, как указатель функции прототипирован в version.h
как void (* zprintf)(const char *, ...);
. Когда он ссылается в приложении, он начнет выполнение везде, где он указывает, что еще должно быть определены. В version.c
уведомление в функции board_init()
, где zprintf
назначается уникальная функция (подпись функции которой соответствует) в зависимости от версии, определенной в version.h
zprintf = &printf;
, zprintf вызывает printf для целей отладки или zprintf = &noprint;
zprintf просто возвращает и не будет запускать ненужный код
Запуск кода будет выглядеть следующим образом:
#include "version.h"
#include <stdlib.h>
int main()
{
// Must run board_init(), which assigns the function
// pointer to an actual function
board_init();
void *ptr = malloc(100); // Allocate 100 bytes of memory
// malloc returns NULL if unable to allocate the memory.
if (ptr == NULL)
{
zprintf("Unable to allocate memory\n");
return 1;
}
// Other things to do...
return 0;
}
В приведенном выше коде будет использоваться printf
в режиме отладки или ничего не делать, если в режиме деблокирования. Это намного проще, чем пройти весь проект и комментировать или удалять код. Все, что мне нужно сделать, это изменить версию в version.h
, а код сделает все остальное!
Одним из больших применений для указателей функций в C является вызов функции, выбранной во время выполнения. Например, библиотека времени выполнения C имеет две процедуры: qsort и bsearch, которые принимают указатель на функцию, вызываемую для сравнения двух отсортированных элементов; это позволяет сортировать или искать, соответственно, что угодно, на основе любых критериев, которые вы хотите использовать.
Очень простой пример, если есть одна функция, называемая print (int x, int y), которая, в свою очередь, может потребовать вызова функции add () или sub (), которые имеют похожие типы, тогда мы будем do, мы добавим один аргумент указателя на функцию print (), как показано ниже: -
int add()
{
return (100+10);
}
int sub()
{
return (100-10);
}
void print(int x, int y, int (*func)())
{
printf("value is : %d", (x+y+(*func)()));
}
int main()
{
int x=100, y=200;
print(x,y,add);
print(x,y,sub);
return 0;
}
Указатель функции обычно определяется typedef и используется как param & amp; возвращаемое значение,
Выше ответов уже много объяснено, я просто приведу полный пример:
#include <stdio.h>
#define NUM_A 1
#define NUM_B 2
// define a function pointer type
typedef int (*two_num_operation)(int, int);
// an actual standalone function
static int sum(int a, int b) {
return a + b;
}
// use function pointer as param,
static int sum_via_pointer(int a, int b, two_num_operation funp) {
return (*funp)(a, b);
}
// use function pointer as return value,
static two_num_operation get_sum_fun() {
return ∑
}
// test - use function pointer as variable,
void test_pointer_as_variable() {
// create a pointer to function,
two_num_operation sum_p = ∑
// call function via pointer
printf("pointer as variable:\t %d + %d = %d\n", NUM_A, NUM_B, (*sum_p)(NUM_A, NUM_B));
}
// test - use function pointer as param,
void test_pointer_as_param() {
printf("pointer as param:\t %d + %d = %d\n", NUM_A, NUM_B, sum_via_pointer(NUM_A, NUM_B, &sum));
}
// test - use function pointer as return value,
void test_pointer_as_return_value() {
printf("pointer as return value:\t %d + %d = %d\n", NUM_A, NUM_B, (*get_sum_fun())(NUM_A, NUM_B));
}
int main() {
test_pointer_as_variable();
test_pointer_as_param();
test_pointer_as_return_value();
return 0;
}
полезны во многих ситуациях, например:
This->lpVtbl->AddRef(This);
AddRef является указателем на функцию. Начиная с нуля функция имеет некоторый адрес памяти от того места, где они начинают выполняться. В языке сборки они называются (вызывают «адрес памяти функции»). Теперь вернитесь к функции C Если у функции есть адрес памяти, тогда с ними можно управлять указателями в C.So. По правилам C
1. Сначала вам нужно объявить указатель на функцию 2.Pass адрес желаемой функции
**** Примечание-> функции должны быть одного типа ****
Эта простая программа будет иллюстрировать каждую вещь.
#include<stdio.h>
void (*print)() ;//Declare a Function Pointers
void sayhello();//Declare The Function Whose Address is to be passed
//The Functions should Be of Same Type
int main()
{
print=sayhello;//Addressof sayhello is assigned to print
print();//print Does A call To The Function
return 0;
}
void sayhello()
{
printf("\n Hello World");
}
[/g0] После этого позволяет увидеть, как машина понимает Them.Glimpse машинной инструкции вышеуказанной программы в 32-битной архитектуре.
В области красных отметок показано, как происходит обмен и хранение адреса в eax. Затем их команда вызова на eax. eax содержит желаемый адрес функции
Так как указатели на функции часто набирают обратные вызовы, вы можете захотеть просмотреть безопасные обратные вызовы типа . То же самое относится к точкам входа и т. Д. Функций, которые не являются обратными вызовами.
C довольно изменчив и прощает одновременно:)
pshufb
, он медленный, поэтому более ранняя реализация все еще быстрее. x264 / x265 используют это широко и с открытым исходным кодом. – Peter Cordes 30 August 2015 в 02:22