C, PHP, VB, .NET

Дневникът на Филип Петров


* Указател към функция

Публикувано на 14 ноември 2008 в раздел С/С++.

Указателите към функции водят до някои много интересни и ефективни програмни техники. Както подсказва името им – това са указатели, които сочат към адреса в паметта, където е записана дадена функция. Тук трябва да отбележим, че указателите към функции не са нововъведение от С++, а са известни още от много компилатори за С. Оказват се особено подходящи при извикване на „callbacks“ и за създаване на събития (events). Callback наричаме функция, която програмистът не е извикал изрично в програмния код. За тяхното изпълнение се грижи друга функция, която е приела указател към нейния адрес.

Дефиницирнето на указатели към фунции се получава по следният начин:

	<тип връщан резултат> (*<име>) (<входни параметри>);

Пример: Дефинираме две функции max и min, които предаваме чрез указател на друга функция show, която ги изпълнява:

	#include "stdafx.h"
	#include "iostream.h"

	void max(int a, int b){
		cout << "the bigger number is: ";
		cout << ((a>b)?a:b);
		cout << endl;
	}

	void min(int a, int b){
		cout << "the lower number is: ";
		cout << ((a<b)?a:b);
		cout << endl;
	}

	// show приема като трети параметър указател към
	// функция, която е от тип void и е с входни параметри
	// две числа от тип int
	void show(int x, int y, void(*pFunc)(int, int)){
		pFunc(x, y);
	}

	void main()
	{
		// Създаваме указател
		void(*ptr2func)(int, int);
		// Насочваме указателя към функцията max
		ptr2func = max;
		show(2 , 3 , ptr2func);
		// Сега насочваме указателя към min
		ptr2func = min;
		show(2 , 3 , ptr2func);
	}

От примера веднага става ясно, че синтаксисът не е много удобен за работа. Тук на помощ идва typedef, чрез който можем да зададем ново име на типа. Указателя от горният пример може да бъде създаден като тип по следния начин:

	typedef void(*COMPARE)(int, int);
	// Сега можем да създаваме обекти от тип COMPARE
	COMPARE ptr2func;

Задача: Създайте функции plus, minus, multiply и divide, които приемат две числа от тип double и връщат като резултат съответно тяхния сбор, разлика, умножение и деление. Създайте указатели към тези функции и ги изпратете последователно като параметър на функция show, която отпечатва резултатът.

 



5 коментара


  1. Rooney каза:

    double plus(double a, double b) {
    return (a+b);
    }

    void show(double a, double b, double(*ptrfunc) (double, double)) {
    double c = ptrfunc(a,b);
    cout << c;
    }

    int main(int argc, char** argv) {
    double (*ptrFunc) (double,double);
    ptrFunc = plus;

    show(14,5,ptrFunc);

    return 0;
    }

    Не трябва ли така да изглежда фунцкията за събиране на числата? Компилаторът дава грешка main.cpp:37: error: `plus' undeclared (first use this function)

  2. В тази поредност ли слагаш функциите в кода? Тази грешка обикновено се получава когато сложиш първо main метода, после функцията, която извикваш.

    Сложи едно double plus(double a, double b); в самото начало на програмата, непосредствено след include частта.

  3. Rooney каза:

    Ами така си е. Точно след инклууда е plus, след това show и накрая main.

  4. Vencislav каза:

    Здравейте имам някоко въпроса относно примера Ви?
    1. pFunc(x, y); В момента pFunc нали пази адрес? Защо да не е *pFunc(x, y), т.е това, което е на адреса?
    2.ptr2func = max; и ptr2func = min; Зашо да не е ptr2func = &max; ptr2func = &min; т,е указателят да сочи към адреса на тази функция?
    3.Как самите функции се съхраняват в паметта?

  5. Здравей Венцислав,

    1. Следните записи са еквивалентни:

    – void (*sayHello)() = sayHello;
    (*sayHelloPtr)();

    – void (*sayHello)() = sayHello;
    sayHelloPtr();

    Просто sayHello e… да го нарека синоним на (*sayHello). Скобите са задължителни – без тях няма същото значение!

    2. Виж 1.

    3. Изключително обширен въпрос е това, с който се слиза на много ниско ниво. Трябва да се обясни архитектурата на Джон Фон Нойман. Най-простичко казано – в рам паметта не се съхраняват само данни, а и инструкции и адреси. Функциите са микс со данни, инструкции и адреси. В края на всяка функция има т.нар. „return адрес“ – къде да прехвърли действието след като функцията е приключила своята работа.

Добави коментар

Адресът на електронната поща няма да се публикува


*