как работают указатели в типе в c? [Дубликат]

Для этой цели вы можете использовать AppCompat.getReferrer () .

1019
задан Yuval Adam 8 May 2009 в 16:49
поделиться

12 ответов

Указатели функций в C

Начнем с основной функции, которую мы будем , указывающей на :

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;
}
1250
ответ дан Tom Zych 17 August 2018 в 09:48
поделиться
  • 1
    Спасибо за отличную информацию. Не могли бы вы добавить некоторые сведения о том, где используются указатели на функции или что они особенно полезны? – Rich.Carpenter 8 May 2009 в 16:55
  • 2
    "functionPtr = & amp; addInt; & quot; также может быть записана (и часто есть) как " functionPtr = addInt; & quot; что также справедливо, так как стандарт говорит, что имя функции в этом контексте преобразуется в адрес функции. – hlovdal 9 May 2009 в 15:39
  • 3
    hlovdal, в этом контексте интересно объяснить, что это то, что позволяет писать функциюPtr = ****************** addInt; – Johannes Schaub - litb 10 May 2009 в 18:54
  • 4
    @ Rich.Carpenter Я знаю, что это слишком поздно, но я полагаю, что другие люди могут воспользоваться этим: Указатели функций полезны для передачи функций как параметров другим функциям . Мне потребовалось много времени, чтобы найти этот ответ по какой-то странной причине. Таким образом, в принципе, он дает C псевдо первоклассную функциональность. – giant91 13 October 2013 в 03:28
  • 5
    @ Rich.Carpenter: указатели на функции хороши для обнаружения CPU во время выполнения. Имейте несколько версий некоторых функций, чтобы использовать SSE, popcnt, AVX и т. Д. При запуске задайте свои указатели на лучшую версию каждой функции для текущего процессора. В вашем другом коде просто вызовите указатель на функцию, вместо того чтобы иметь условные ветви на функциях ЦП во всем мире. Тогда вы можете сделать сложную логику о том, чтобы решить эту проблему, хотя этот процессор поддерживает pshufb, он медленный, поэтому более ранняя реализация все еще быстрее. x264 / x265 используют это широко и с открытым исходным кодом. – Peter Cordes 30 August 2015 в 02:22

Руководство по запуску: как злоупотреблять указателями функций в GCC на машинах x86, компилируя ваш код вручную:

  1. Возвращает текущее значение в регистре EAX
    int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();
    
  2. Запись функции подкачки
    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);
    
  3. Запись счетчика для цикла до 1000, вызов какой-либо функции каждый раз
    ((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
    
  4. Вы даже можете записать рекурсивную функцию, которая до 100
    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);
    
188
ответ дан 2 revs, 2 users 98% 17 August 2018 в 09:48
поделиться
  • 1
    Примечание. Это не работает, если функция предотвращения выполнения данных включена (например, в Windows XP с пакетом обновления 2), потому что строки C обычно не помечены как исполняемые. – SecurityMatt 12 February 2013 в 07:53
  • 2
    Привет, Мэтт! В зависимости от уровня оптимизации GCC часто встраивает строковые константы в сегмент TEXT, поэтому это будет работать даже в более новой версии окон при условии, что вы не запретите этот тип оптимизации. (IIRC, версия MINGW во время моего сообщения более двух лет назад в строковых литералах на уровне оптимизации по умолчанию) – Lee 2 January 2014 в 08:20
  • 3
    может кто-нибудь объяснить, что здесь происходит? Что это за странные выглядящие строковые литералы? – ajay 20 January 2014 в 12:17
  • 4
    @ajay Похоже, он пишет необработанные шестнадцатеричные значения (например, «\ x00» совпадает с «/ 0», они равны 0) в строку, а затем переводит строку в указатель функции C, а затем выполняет указатель функции C, потому что он дьявол. – ejk314 21 February 2014 в 23:27
  • 5
    hi FUZxxl, я думаю, он может отличаться в зависимости от компилятора и версии операционной системы. Вышеприведенный код работает нормально на codepad.org; codepad.org/FMSDQ3ME – Lee 13 March 2014 в 02:48

Одно из моих любимых применений для указателей функций - это дешевые и простые итераторы -

#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);
}
95
ответ дан 2 revs, 2 users 99% 17 August 2018 в 09:48
поделиться
  • 1
    Вы также должны передать указатель на данные, указанные пользователем, если вы хотите каким-то образом извлечь любой результат из итераций (подумайте о закрытии). – Alexei Averchenko 16 September 2012 в 15:32
  • 2
    Согласовано. Все мои итераторы выглядят так: int (*cb)(void *arg, ...). Возвращаемое значение итератора также позволяет мне останавливаться на раннем этапе (если отличное от нуля). – Jonathon Reinhart 30 April 2015 в 12:35
23
ответ дан 3 revs 17 August 2018 в 09:48
поделиться

Указатели функций на 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 по какой-либо причине, все, что нужно сделать, это:

  1. Добавить функцию, которая будет служить в качестве переопределяющего метода length.
  2. Перейдите к «конструктору» и установите указатель на метод переопределения 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, обратитесь к следующим вопросам:

267
ответ дан 3 revs, 2 users 99% 17 August 2018 в 09:48
поделиться
  • 1
    Этот ответ ужасен! Не только это подразумевает, что OO каким-то образом зависит от точечной нотации, но также поощряет подачу мусора в ваши объекты! – Alexei Averchenko 16 September 2012 в 15:30
  • 2
    Это OO все в порядке, но не где-то рядом с OO C-стиля. То, что вы нарушили, - это OO на основе прототипов в стиле Javascript. Чтобы получить OO в стиле C ++ / Pascal, вам необходимо: 1. Создать структуру const для виртуальной таблицы каждого класса с виртуальными членами. 2. Имейте указатель на эту структуру в полиморфных объектах. 3. Вызовите виртуальные методы через виртуальную таблицу и все другие методы напрямую - обычно придерживаясь некоторого соглашения об именах функций ClassName_methodName. Только тогда вы получите те же затраты времени на запуск и хранение, что и на C ++ и Pascal. – Kuba Ober 18 March 2013 в 23:53
  • 3
    Рабочая OO с языком, который не предназначен для OO, всегда является плохим. Если вы хотите OO и все еще имеете C, просто работаете с C ++. – rbaleksandar 4 July 2013 в 16:21
  • 4
    @rbaleksandar Скажите об этом разработчикам ядра Linux. «всегда плохая идея» - это строго ваше мнение, с которым я категорически не согласен. – Jonathon Reinhart 30 April 2015 в 12:31
  • 5
    Мне нравится этот ответ, но не бросайте malloc – cat 29 September 2016 в 14:41

Еще одно полезное использование для указателей функций: безболезненно переключаться между версиями

. Они очень удобны в использовании, когда вам нужны разные функции в разное время или на разных этапах разработки. Например, я разрабатываю приложение на главном компьютере с консолью, но окончательная версия программного обеспечения будет размещена на Avnet ZedBoard (у которого есть порты для дисплеев и консолей, но они не нужны / не нужны для Окончательный релиз). Поэтому во время разработки я буду использовать printf для просмотра сообщений о состоянии и ошибках, но когда я закончил, я не хочу ничего печатать. Вот что я сделал:

version.h

// 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

version.c

#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 просто возвращает и не будет запускать ненужный код

Запуск кода будет выглядеть следующим образом:

mainProg.c

#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, а код сделает все остальное!

21
ответ дан 2 revs 17 August 2018 в 09:48
поделиться
  • 1
    U выдерживает много времени на производительность. Вместо этого вы можете использовать макрос, который включает и отключает раздел кода на основе Debug / Release. – Akshay Immanuel D 11 May 2018 в 13:41

Одним из больших применений для указателей функций в 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;
}
8
ответ дан 2 revs, 2 users 96% 17 August 2018 в 09:48
поделиться

Указатель функции обычно определяется 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 &sum;
}

// test - use function pointer as variable,
void test_pointer_as_variable() {
    // create a pointer to function,
    two_num_operation sum_p = &sum;
    // 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;
}
13
ответ дан Eric Wang 17 August 2018 в 09:48
поделиться
Функциональные указатели

полезны во многих ситуациях, например:

  • Члены COM-объектов являются указателями на функцию ag: This->lpVtbl->AddRef(This); AddRef является указателем на функцию.
  • функция callback, например, функция, определяемая пользователем, для сравнения двух переменных, которые должны передаваться как обратный вызов специальной функции сортировки.
  • очень полезен для реализации плагина и SDK приложения.
-8
ответ дан milevyo 17 August 2018 в 09:48
поделиться

Начиная с нуля функция имеет некоторый адрес памяти от того места, где они начинают выполняться. В языке сборки они называются (вызывают «адрес памяти функции»). Теперь вернитесь к функции 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");
}

enter image description here [/g0] После этого позволяет увидеть, как машина понимает Them.Glimpse машинной инструкции вышеуказанной программы в 32-битной архитектуре.

В области красных отметок показано, как происходит обмен и хранение адреса в eax. Затем их команда вызова на eax. eax содержит желаемый адрес функции

3
ответ дан Mohit Dabas 17 August 2018 в 09:48
поделиться

Так как указатели на функции часто набирают обратные вызовы, вы можете захотеть просмотреть безопасные обратные вызовы типа . То же самое относится к точкам входа и т. Д. Функций, которые не являются обратными вызовами.

C довольно изменчив и прощает одновременно:)

0
ответ дан Tim Post 17 August 2018 в 09:48
поделиться
1
ответ дан Richard Chambers 6 September 2018 в 07:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: