Так как у функции тоже есть адрес в памяти, то в си возможно получить указатель на функцию. Этот указатель потом можно передать в другую функцию как аргумент, сохранить в поле структуры и потом вызвать.
Синтаксис объявления переменной типа указатель на функцию:
RETURN_TYPE (*func_name) (FUNC_ARGUMENTS);
RETURN_TYPE (*funcs_array[]) (FUNC_ARGUMENTS); // массив указателей на функции
Рассмотрим пример:
#include <stdio.h>
void hello(void) {
printf("Hello");
}
void world(void) {
printf("World");
}
int main(void) {
void (*func_ptr)(void); // func_ptr - это ИМЯ переменной для указателя на функцию
func_ptr = hello;
func_ptr();
func_ptr = world;
func_ptr();
return 0;
}
/**
Пример с массиво указателей на функции
**/
#include <stdio.h>
void add(int x, int y){
printf("x+y=%d \\n", x+y);
}
void subtract(int x, int y){
printf("x+y=%d \\n", x-y);
}
void multiply(int x, int y){
printf("x*y=%d \\n", x*y);
}
int main(void)
{
const int LEN = 3;
int a = 10;
int b = 5;
void (*operations[LEN])(int, int) = {add, subtract, multiply};
for(int i=0; i<LEN;i++) {
operations[i](a, b); // вызов функции по указателю
}
return 0;
}
Передача указателя на функцию в другую функцию
#include <stdio.h>
int calc(int a, int b, int (*op)(int)) {
return op(a) + op(b);
}
int inc(int a) {
return a + 1;
}
int shift(int a) {
return a << 1;
}
int main(void) {
printf("res=%d\\n", calc(1,2, inc));
printf("res=%d\\n", calc(1,2, shift));
return 0;
}
Для того чтобы не писать такое сложное объявление функции используют typedef:
typedef тип_возвращаемого_значения (*имя_указ)(список_параметров_функции);
Например, для прошлого кода это будет:
#include <stdio.h>
typedef int (*Operation)(int);
int calc(int a, int b, Operation op) { // теперь все просто
return op(a) + op(b);
}
int inc(int a) {
int res = a + 1;
printf("inc(%d) = %d\\n", a, res);
return res;
}
int shift(int a) {
int res = a << 1;
printf("shift(%d) = %d\\n", a, res);
return res;
}
int main(void) {
printf("res=%d\\n", calc(1,2, inc));
printf("res=%d\\n", calc(1,2, shift));
return 0;
}
Если функцию можно передавать как аргумент функции, то можно создавать абстракции на основе функций, а не только данных.
Например
#include <stdio.h>
double sum_nums(double a, double b) {
if (a > b) { return 0; }
double acc = 0;
while (a < b) {
acc += a;
a++;
}
return acc;
}
double sum_nums_f(double a, double b, double (*map)(double)) {
if (a > b) { return 0; }
double acc = 0;
while (a < b) {
acc += map(a);
a++;
}
return acc;
}
int sum_nums_f_step(double a, double b, double (*map)(double), double (*next)(double)) {
if (a > b) { return 0; }
double acc = 0;
while (a < b) {
acc += map(a);
a = next(a);
}
return acc;
}
// Тогда можно вычислить число Пи
double pi_map(double x) {
return 1.0 / x*(x+1);
}
double pi_next(double x) {
return x + 4;
}
int main(void) {
double pi = sum_nums_f_step(1, 1000, pi_map, pi_next);
print("pi=%f\\n", pi);
return 0;
}
Далее - можно создавать еще более сложные функции, например map и reduce.
В sort используется указатель на void и отправлять в сортировку можно любые данные.
#include "BearLibTerminal.h"
typedef struct GameObject_ {
char tile;
int x;
int y;
void (*GoUp)(struct GameObject_*);
void (*GoDown)(struct GameObject_*);
void (*GoRight)(struct GameObject_*);
void (*GoLeft)(struct GameObject_*);
} GameObject;
void GoUpSlow(GameObject* player) {
player->y -= 1;
}
void GoDownSlow(GameObject* player) {
player->y += 1;
}
void GoRightSlow(GameObject* player) {
player->x += 1;
}
void GoLeftSlow(GameObject* player) {
player->x -= 1;
}
GameObject* NewPlayer() {
GameObject* player = malloc(sizeof(GameObject));
player->tile = '@';
player->x = 2;
player->y = 2;
player->GoUp = GoUpSlow;
player->GoDown = GoDownSlow;
player->GoLeft = GoLeftSlow;
player->GoRight = GoRightSlow;
return player;
}
void ShuffleMovement(GameObject* go) {
go->GoUp = GoLeftSlow;
go->GoDown = GoRightSlow;
go->GoLeft = GoUpSlow;
go->GoRight = GoDownSlow;
}
void NormalizeMovement(GameObject* go) {
go->GoUp = GoUpSlow;
go->GoDown = GoDownSlow;
go->GoLeft = GoLeftSlow;
go->GoRight = GoRightSlow;
}
typedef struct World_ {
GameObject *player;
} World;
World* NewWorld() {
World *world = malloc(sizeof(World));
return world;
}
void DrawWorld(const World* w) {
terminal_put(w->player->x, w->player->y, w->player->tile);
terminal_put(5,5,'%');
terminal_put(10,10,'*');
}
void ApplyMovement(GameObject* go, int code) {
if (code == TK_W) {
go->GoUp(go);
}
if (code == TK_S) {
go->GoDown(go);
}
if (code == TK_A) {
go->GoLeft(go);
}
if (code == TK_D) {
go->GoRight(go);
}
}
void loop(World* world, int code) {
GameObject* p = world->player;
ApplyMovement(p, code);
if (p->x == 5 && p->y == 5) {
ShuffleMovement(p);
}
if (p->x == 10 && p->y == 10) {
NormalizeMovement(p);
}
DrawWorld(world);
}
int main() {
World* world = NewWorld();
world->player = NewPlayer();
terminal_open();
DrawWorld(world);
terminal_refresh();
while (1) {
int code = terminal_read();
terminal_clear();
if (code == TK_CLOSE) {
break;
}
loop(world, code);
terminal_refresh();
}
terminal_close();
}