Conception du codage C - pointeurs de fonction?

24

J'ai un PIC18F46K22 et je le programme avec le compilateur XC8. Au final, j'aurai un système comme un pc avec stdinet stdout. Donc, dans la boucle principale, il y aura une fonction qui vérifie s'il y a une nouvelle entrée. S'il y a une entrée, une fonction sera appelée en conséquence. Ainsi, par exemple, lorsque j'entre un A stdin, le PIC exécute une fonction similaire function_Aà celle function_Bqui est appelée lorsque j'entre un B.

Lorsque le PIC est terminé avec la fonction, je veux que la nouvelle entrée soit envoyée à la fonction. Donc, lorsque vous appuyez sur A ouvre l'émetteur RS232, à partir de ce moment, chaque entrée sera envoyée via RS232. Au final, le projet est un éditeur de texte autonome. Ainsi, lorsque vous appuyez sur A pour ouvrir le système de fichiers, à partir de ce moment, vous ne modifiez plus de texte mais vous parcourez une liste de fichiers. Cela signifie qu'appuyer sur Haut et Bas signifie quelque chose de différent que dans l'environnement d'édition de texte.

J'ai beaucoup réfléchi à la façon de programmer cela en C. J'ai pensé cela hier soir et j'aimerais savoir si c'est possible et si oui, comment. Ce que je veux faire c'est:

  • La mainfonction appelle une fonction commefunction_A
  • function_Achange une variable globale function_addren pointeur d'adresse de la fonctionin_function_A
  • À partir de ce moment, mainappelle la fonction function_addrlorsqu'il y a une nouvelle entrée.

Donc, ce dont j'ai besoin, c'est d'une mainfonction qui vérifie si elle function_addrest nulle. Si c'est le cas, une fonction «normale» devrait être appelée, comme function_A. Si ce n'est pas le cas, la fonction at function_addrdoit être appelée. J'ai également besoin d'un function_Aqui change le function_addren un pointeur in_function_A.

Remarque: lorsque la fonction de système de fichiers doit être fermée, elle is_function_Adoit simplement passer function_addrà 0.

Donc, fondamentalement, ma question est de savoir comment puis-je

  • Récupère l'adresse d'une fonction (et la stocke dans une variable)
  • Appeler une fonction à une adresse spécifiée

la source
6
Hors sujet. Appartient à stackoverflow.com.
user207421
Pour moi, une approche de machine d'état est beaucoup moins risquée que de traiter avec des pointeurs de fonction. Votre octet d'entrée est transmis à une structure de machine à états (qui peut être aussi simple qu'un boîtier de commutation) qui passe à différents bits de code en fonction de la ou des variables d'état. De plus, vous ne savez pas exactement d'où vient votre stdin (ce n'est pas vraiment important, mais je suis curieux).
Adam Lawrence
9
@EJP Je ne suis pas d'accord. Ce n'est pas parce qu'il y a un chevauchement qu'il doit être sur l'un ou l'autre site. Il pose des questions liées à la conception et à la programmation de systèmes embarqués de bas niveau, qui semblent sur le sujet dans les deux cas.
Kortuk
Les machines à états peuvent être basées sur l'indirection de fonction: l'appel à la fonction de transition d'état renvoie une nouvelle fonction de transition d'état.
Kaz
1
Permet d'avoir cette discussion ici .

Réponses:

21

Une fonction

int f(int x){ .. }

Récupère l'adresse d'une fonction (et la stocke dans une variable)

int (*fp)(int) = f;

Appeler une fonction à une adresse spécifiée

int x = (*fp)( 12 );
Wouter van Ooijen
la source
3
+1, évident dans le monde C et ASM, mais pas si évident pour ceux qui ont commencé avec les langages OO.
Anindo Ghosh
4
L'appel fp peut également être écrit comme 'int x = fp (12);' pour augmenter la lisibilité.
Rev1.0
1
Je dois admettre que, travaillant principalement en C # et en Java actuellement, ces bons vieux pointeurs de fonction de C me manquent parfois. Ils sont dangereux lorsqu'ils ne sont pas faits correctement, et la plupart des tâches peuvent être accomplies d'autres manières, mais ils sont certainement utiles ces quelques fois où ils sont vraiment utiles.
un CVn du
@ MichaelKjörling: C'est vrai. Je bascule constamment entre la programmation C et C # intégrée (même en implémentant un code similaire pour la communication entre l'appareil et le PC) et aussi puissant que C # puisse être, j'aime toujours beaucoup C.
Rev1.0
2
fp(12)n'augmente pas la lisibilité (*fp)(12). Ils ont une lisibilité différente. (*fp)(12)rappelle au lecteur qu'il fps'agit d'un pointeur et qu'il ressemble également à la déclaration de fp. Dans mon esprit, ce style est associé à d'anciennes sources, comme les internes X11, et K&R C.Une raison possible qu'il pourrait être associé à K&R C est parce qu'en ANSI C, il est possible de déclarer en fputilisant la syntaxe de fonction, si fpc'est un paramètre: int func(int fp(double)) {}. Dans K&R C, cela aurait été le cas int func(fp) int (*fp)(double); { ... }.
Kaz
17

Bien que la réponse de Wouters soit absolument correcte, cela peut être plus convivial pour les débutants et un meilleur exemple concernant votre question.

int (*fp)(int) = NULL;

int FunctionA(int x){ 
    return x + x;
}

int FunctionB(int x){ 
    return x * x;
}

void Example(void) {
    int x = 0;

    fp = FunctionA;
    x = fp(3);  /* after this, x == 6 */

    fp = FunctionB;
    x = fp(3);  /* after this, x == 9 */
}

S'il n'est pas évident que le pointeur de fonction a réellement une adresse de fonction cible assignée, il est sage d'effectuer une vérification NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
Rev1.0
la source
8
+1 pour la vérification nulle, mais notez que le compilateur peut ne pas garantir que la valeur à l'adresse en RAM qui fpse trouve occuper est en fait NULL au départ. Je ferais int (*fp)(int) = NULL;une déclaration et une initialisation combinées pour être certain - vous ne voulez pas sauter à un emplacement de mémoire aléatoire à cause d'une valeur maladroite qui se trouve justement être là. Ensuite, affectez simplement fpcomme dans votre Example()fonction.
un CVn du
1
Merci pour le commentaire, j'ai ajouté l'initialisation NULL.
Rev1.0
4
@ MichaelKjörling bon point, mais s'il fps'agit d'une "variable globale" (comme dans le code ci-dessus), alors il est garanti d'être initialisé à NULL(sans avoir à le faire explicitement).
Alok