Пожалуйста, посмотрите пример кода ниже:
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function(e) {
$('#blah').attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
}
$("#imgInp").change(function() {
readURL(this);
});
Кроме того, вы можете попробовать этот образец здесь .
Начнем с базовой функции, которая будет указывать на :
int addInt(int n, int m) {
return n+m;
}
Прежде всего, давайте определим указатель на функцию, которая получает 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;
}
Одно из моих любимых применений указателей на функции - это дешевые и простые итераторы -
#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);
}
Указатели функций можно легко объявлять, если у вас есть основные деклараторы:
ID
: ID - это * D
: указатель D на D (<параметры>)
: функция D, принимающая <
параметры >
возвращающая В то время как D - еще один декларатор, созданный с использованием тех же правил. В конце концов где-то он заканчивается ID
(см. пример ниже), который является именем объявленного объекта. Давайте попробуем создать функцию, принимающую указатель на функцию, которая ничего не берет и возвращает int, и возвращает указатель на функцию, принимающую char и возвращающую int. С type-defs это примерно так
typedef int ReturnFunction(char);
typedef int ParameterFunction(void);
ReturnFunction *f(ParameterFunction *p);
Как видите, довольно легко создать его, используя typedefs. Без typedef это не сложно с последовательным применением вышеуказанных правил декларатора. Как видите, я пропустил ту часть, на которую указывает указатель, и то, что возвращает функция. Это то, что появляется в самом левом углу объявления и не представляет интереса: оно добавляется в конце, если декларатор уже создан. Давайте сделаем это. Построим его последовательно, сначала многословно - покажем структуру с помощью [
и ]
:
function taking
[pointer to [function taking [void] returning [int]]]
returning
[pointer to [function taking [char] returning [int]]]
Как видите, можно полностью описать тип, добавляя деклараторы один за другим. Строительство можно вести двумя способами. Один - снизу вверх, начиная с самого правильного (уходит) и продвигаясь до идентификатора. Другой способ - сверху вниз, начиная с идентификатора и заканчивая листьями. Я покажу оба пути.
Построение начинается с объекта справа: возвращаемого объекта - функции, принимающей char. Чтобы деклараторы были различны, я собираюсь пронумеровать их:
D1(char);
Параметр char вставлен напрямую, поскольку это тривиально. Добавление указателя на декларатор путем замены D1
на * D2
. Обратите внимание, что нам нужно заключить в круглые скобки * D2
. Это можно узнать, посмотрев приоритет оператора * -
и оператора вызова функции ()
. Без скобок компилятор прочитал бы его как * (D2 (char p))
. Но это, конечно, уже не будет простой заменой D1 на * D2
. Круглые скобки вокруг деклараторов всегда разрешены. Так что вы не сделаете ничего плохого, если добавите их слишком много.
(*D2)(char);
Тип возврата завершен! Теперь давайте заменим D2
функцией декларатора функции , принимающей <параметры>
, возвращающей , то есть D3 (<параметры>)
, который мы сейчас.
(*D3(<parameters>))(char)
Обратите внимание, что скобки не нужны, поскольку мы хотим, чтобы D3
на этот раз был декларатором функции, а не указателем. Большой, осталось только его параметры. Параметр выполняется точно так же, как и возвращаемый тип, только с заменой char
на void
. Итак, я скопирую его:
(*D3( (*ID1)(void)))(char)
Я заменил D2
на ID1
, так как мы закончили с этим параметром (это уже указатель на функцию - нет необходимости в другом декларатор). ID1
будет именем параметра. Теперь, как я сказал выше, в конце добавляется тип, который изменяют все эти деклараторы - тот, который появляется в самом левом углу каждого объявления. Для функций это становится возвращаемым типом. Для указателей, указывающих на тип и т.д. Интересно, когда тип записывается, он будет отображаться в обратном порядке, справа :) В любом случае, его подстановка дает полное объявление. Оба раза int
конечно.
int (*ID0(int (*ID1)(void)))(char)
В этом примере я вызвал идентификатор функции ID0
.
Это начинается с идентификатора в самом левом углу описания типа, обертывая этот декларатор, когда мы идем через правый. Начните с функции , принимающей <
параметров >
, возвращающей
ID0(<parameters>)
Следующим в описании (после «возврата») был указатель на . Давайте добавим его:
*ID0(<parameters>)
Затем следующая функция , принимающая <
параметры >
, возвращающая . Параметр представляет собой простой символ, поэтому мы сразу же добавляем его, поскольку он действительно тривиален.
(*ID0(<parameters>))(char)
Обратите внимание на добавленные нами круглые скобки, так как мы снова хотим, чтобы сначала связывался *
, и , затем (char)
. В противном случае он прочитал бы функцию , принимающую <
параметры >
, возвращающую функцию ... . Нет, функции, возвращающие функции, даже не разрешены.
Теперь нам просто нужно указать <
параметры >
. Я покажу краткую версию вывода, так как я думаю, что вы уже знаете, как это сделать.
pointer to: *ID1
... function taking void returning: (*ID1)(void)
Просто поместите int
перед деклараторами, как мы делали с восходящим потоком, и мы закончены
int (*ID0(int (*ID1)(void)))(char)
Что лучше: снизу вверх или сверху вниз? Я привык работать снизу вверх, но некоторым людям удобнее работать сверху вниз. Думаю, это дело вкуса. Кстати, если вы примените все операторы в этом объявлении, вы получите int:
int v = (*ID0(some_function_pointer))(some_char);
Это хорошее свойство объявлений в C: объявление утверждает, что если эти операторы используются в выражении с использованием идентификатора, то оно дает тип, который находится слева. То же самое и с массивами.
Надеюсь, вам понравился этот небольшой урок! Теперь мы можем ссылаться на это, когда люди задаются вопросом о странном синтаксисе объявления функций. Я старался использовать как можно меньше внутренних компонентов C. Не стесняйтесь редактировать / исправлять что-то в нем.
Не стесняйтесь редактировать / исправлять что-то в нем. Не стесняйтесь редактировать / исправлять что-то в нем.Поскольку указатели на функции часто являются типизированными обратными вызовами, вы можете захотеть взглянуть на типобезопасные обратные вызовы . То же самое относится к точкам входа и т. Д. Функций, которые не являются обратными вызовами.
C довольно непостоянен и в то же время снисходителен :)
Указатели функций в C могут использоваться для выполнения объектно-ориентированного программирования в C.
Например, на C написаны следующие строки:
String s1 = newString();
s1->set(s1, "hello");
Да, - >
и отсутствие оператора new
бесполезны, но, похоже, это означает, что мы устанавливаем текст некоторого класса String
равным ] "hello"
.
Используя указатели функций, можно эмулировать методы в C .
Как это достигается?
String
class на самом деле является структурой
с кучей указателей на функции, которые действуют как способ имитации методов. Ниже приводится частичное объявление класса 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;
}
Можно заметить одну вещь: не существует концепции экземпляра объекта и наличия методов, которые на самом деле часть объекта, поэтому "собственный объект" должен передаваться при каждом вызове. (И внутренняя
- это просто скрытая структура
, которая была опущена в листинге кода ранее - это способ скрыть информацию, но это не относится к указателям на функции. )
Таким образом, вместо возможности выполнить s1-> set ("hello");
, нужно передать объект для выполнения действия с s1-> set (s1, "Привет")
.
С этим второстепенным объяснением, которое необходимо передать ссылкой на вас, мы перейдем к следующей части, которая является наследованием в C .
Допустим, мы хотим сделать подкласс String
, скажем ImmutableString
. Чтобы сделать строку неизменной, метод set
будет недоступен, при этом будет сохранен доступ к get
и length
, а также заставить «конструктор» принять a 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);
По сути, для всех подклассов доступные методы снова являются указателями на функции. В это время,