У вас есть указатель функции с назначенным значением параметра в C?
Я хочу указать на что-то вроде add(2, 1)
а затем вызывать его без необходимости указывать параметры. Есть ли способ сделать это с помощью указателей функций?
Ответы
Ответ 1
В качестве комментария к вашему вопросу Басиле Старынкевич предложил прочитать о закрытии. По своей сути закрытие объединяет функцию, которая должна быть вызвана с некоторым контекстом, упомянутым в литературе как среда функций.
Объектно-ориентированные языки имеют аналогичную концепцию, называемую делегатами, где конкретный экземпляр может стать средой для более позднего вызова, где вызывающий сайт не имеет прямого знания об основном объекте. Языки, поддерживающие блокировки, автоматически автоматически захватывают или "закрывают" привязки, предоставляемые в среде. Это может показаться странным языком, но это может быть выразительным и полезным, поскольку мотивация вашего вопроса предполагает.
Ниже приведен простой пример концепции замыкания при работе в программе на C, где программист должен непосредственно направлять захваты.
Функция, которую вы хотите в конечном итоге вызвать, плюс некоторый передний элемент
#include <stdio.h>
int
add_two_numbers(int a, int b)
{
return a+b;
}
Закрытие - это функция, которую можно назвать объединенной с ее средой. В C функция, которую нужно вызвать, является указателем на функцию. Обратите внимание, что параметры f
совпадают с параметрами add_two_numbers
.
typedef struct {
struct { /* environment */
int a;
int b;
} env;
int (*f)(int, int); /* function to be called */
} closure;
Ср нравится создавать замыкание, т.е. Настраивать ассоциацию значений параметров для передачи с вызываемой функцией, когда мы будем готовы это сделать. В этом простом примере make_adder
оставляет проблему выделения пространства для закрытия вызывающему.
void
make_adder(int a, int b, closure *c)
{
c->env.a = a;
c->env.b = b;
c->f = add_two_numbers;
}
Теперь, когда вы знаете, как создать одно из наших простых замыканий, вы вызываете или вызываете его, как в
int invoke_closure(closure *c)
{
return c->f(c->env.a, c->env.b);
}
Наконец, использование будет выглядеть так:
int main(void)
{
closure c;
make_adder(2, 1, &c);
printf("The answer is %d.\n", invoke_closure(&c));
return 0;
}
с выходом
The answer is 3.
Дальнейшее чтение
Ответ 2
Хотя короткий ответ "нет", есть способы выполнить то, что вы хотите сделать.
Первое, что нужно помнить, - это add
два параметра. Независимо от того, что вы делаете, вы должны предоставить ему два параметра.
Второе, что нужно помнить, это то, что если вы хотите вызвать функцию, которая в конечном итоге вызывает add(2, 1)
, вы должны хранить или жестко закодировать значения 2
и 1
где-нибудь, чтобы их можно было использовать.
Есть несколько способов сделать это, о чем я могу думать.
-
Создайте функцию-обертку, которая вызывает add(2, 1)
и пусть ваш код вызывает функцию-оболочку.
int add2And1()
{
return add(2, 1);
}
а затем используйте add2And1
в вашем вызове.
-
Создайте функцию обертки, которая опирается на глобальные данные. Сначала установите глобальные данные перед использованием функции обертки.
int param1 = 0;
int param2 = 0;
int addParams()
{
return add(param1, param2);
}
а затем использовать:
param1 = 2;
param2 = 1;
в вашем коде перед вызовом addParams
.
Ответ 3
int add(int a, int b) { return a + b; }
Нет, у вас не может быть указателя для add
с назначенными значениями параметров для него. Однако вы можете сделать это:
int add_2_1() { return add(2, 1); }
однако я не вижу, насколько это полезно...
Ответ 4
Расширение GCC позволит вам сделать это:
int func(int addend, int (*next)(int (*f2)(int))
{
int add(int addend2) { return addend + addend2; }
next(add);
}
Однако если вы попытаетесь сделать:
int (*)(int) func(int addend)
{
int add(int addend2) { return addend + addend2; }
return add;
}
функция не может использоваться, потому что func(3)(3)
выполняет батут * в стеке, который уже был освобожден. Это в значительной степени самое неопределенное поведение.
* Батут - это небольшой фрагмент кода, который сразу же переходит к другому фрагменту кода. В этом случае батут выглядит примерно так
mov rax, 0xfuncstackframeaddress
mov r10, rax
lea rax, [func.add]
jmp rax
Конечно, есть много способов сделать это, и неважно, какой GCC использует. GCC решает проблему того, как передать параметр указателем функции, написав динамический код. Независимо от того, какой код был там написан, в следующий раз глубина вызова функции будет достаточно глубокой, будет стекать фрейм стека.