Jste zde

Méně známé skutečnosti o C a C++: Syntaxe ukazatelů na funkce

O existenci ukazatelů na funkce většina programátorů v C a C++ ví, i když je třeba příliš nepoužívá. V následujícím příspěvku bych chtěl upozornit na některá syntaktická pozlátka, která lze při práci s ukazateli na funkce použít.

Vše, co zde bude zmíněno, se vztahuje jak na C99, tak na C++98 (viz příslušné normy). U čtenáře se předpokládá, že jisté základní znalosti, co se týče ukazatelů na funkce, má :).

Uložení ukazatele na funkci

Mějme definován následující ukazatel na funkci:

size_t (*fp)(const char *);

Očekávané (ve smyslu toho, jak se pracuje s ukazateli) přiřazení hodnoty tomuto ukazateli je

fp = &strlen;

Podobně jako u polí lze ale operátor získání adresy (&) vynechat. Tudíž, i následující konstrukce je platná:

fp = strlen;

Jistým odůvodněním je to, že samotný identifikátor funkce už je vlastně adresa (přesněji adresa + typ, protože dozajista víme, že ukazatel v C je tvořen adresou a typem; samotná adresa je poměrně k ničemu, viz ukazatel na void, přes který nelze do paměti přistoupit, protože neznáme počet bajtů, které máme číst).

Nepřímé volání funkce

Když už máme ten ukazatel na funkci, tak ji pojďme zavolat. První ze způsobů je následující:

printf("%lu\n", (*fp)("test"));

Všimněte si, že závorky kolem *fp jsou nutné; operátor volání funkce () má totiž vyšší prioritu než operátor dereference *. Pokud ale hvězdičku vynecháme, tak lze psát jen:

printf("%lu\n", fp("test"));

Oba způsoby jsou ekvivalentní (hodnota fp je už vlastně adresa funkce).

Parametr typu ukazatel na funkci

Na konci příspěvku bych chtěl upozornit ještě na jednu zajímavost, o které se málo ví. Klasická deklarace funkce, která má jako jeden z parametrů ukazatel na funkci, je následující:

void qsort(void *base, size_t nmemb, size_t size,
	int (*compar)(const void *, const void *));

Jedná se deklaraci známé funkce qsort() ze standardní knihovny. Pokud chceme řadit pole řetězců, tak jako čtvrtý parametr lze předat (ukazatel na) funkci strcmp(). Norma C99 nám ale umožňuje tento zápis zjednodušit:

void qsort(void *base, size_t nmemb, size_t size,
	int compar(const void *, const void *));

Každopádně, toto již může být pro některé matoucí, proto v tomto případě doporučuji hvězdičku a závorky nevynechávat.

Komentáře

Jeste vetsi legrace je se STT_GNU_IFUNC. Je to GNU extension ELF symbolu, Symbol
tohoto typu je adresa funkce, ktera se zavola a vrati pointer na opravdovou funkci,
co se provede. Pouziva se to treba v glibc u nekterych mem* a str* funkci,
aby se volalo treba odpovidajici memmove() podle dostupnosti SSE{2,3} insns.

Pouziti vypada napr. takto:

#include <stdio.h>
#include <time.h>
 
static void
func1 (void)
{
  puts ("func1");
}
 
static void
func2 (void)
{
  puts ("func2");
}
 
typedef void (*func_t) (void);
 
asm (".type gnu_ifunc, @gnu_indirect_function");
 
static func_t
gnu_ifunc (void)
{
  puts ("resolver");
  return time (NULL) & 1 ? func1 : func2;
}
 
int
main (void)
{
  puts ("main");
  gnu_ifunc ();
  return 0;
}

Přidat komentář