Progetto di codifica C - puntatori a funzioni?


24

Ho un PIC18F46K22 e lo programma con il compilatore XC8. Alla fine, avrò un sistema come un pc con stdine stdout. Quindi nel loop principale ci sarà una funzione che sta verificando se ci sono nuovi input. Se c'è input, una funzione verrà chiamata di conseguenza. Quindi, ad esempio, quando inserisco una A stdin, il PIC eseguirà una funzione simile function_Ainvece della function_Bquale viene chiamata quando inserisco una B.

Quando il PIC ha terminato la funzione, voglio che il nuovo input venga inviato alla funzione. Quindi quando si preme A si apre il trasmettitore RS232, da quel momento ogni ingresso verrà inviato su RS232. Alla fine il progetto è un editor di testo autonomo. Quindi quando si preme A si apre il file system, da quel momento non si modifica più il testo ma si guarda attraverso un elenco di file. Ciò significa che premendo Su e Giù si intende qualcosa di diverso rispetto all'ambiente di modifica del testo.

Ho riflettuto molto su come programmare questo in C. L'ho pensato ieri sera e vorrei sapere se è possibile e se sì, come. Quello che voglio fare è:

  • La mainfunzione chiama una funzione comefunction_A
  • function_Acambia una variabile globale function_addrnel puntatore di indirizzo della funzionein_function_A
  • Da quel momento, mainchiama la funzione function_addrquando c'è un nuovo input.

Quindi quello di cui ho bisogno è una mainfunzione che controlla se function_addrè zero. Se lo è, una funzione 'normale' dovrebbe essere chiamata, come function_A. In caso contrario, è function_addrnecessario chiamare la funzione at . Ho anche bisogno di un function_Ache cambi function_addrin un puntatore a in_function_A.

Nota: quando la funzione del filesystem dovrebbe essere chiusa, is_function_Adovrebbe passare function_addra 0.

Quindi in sostanza la mia domanda è come posso

  • Ottieni l'indirizzo di una funzione (e memorizzalo in una variabile)
  • Chiama una funzione a un indirizzo specificato

6
Fuori tema. Appartiene a stackoverflow.com.
user207421

Per me, un approccio basato sulla macchina a stati è molto meno rischioso rispetto alla gestione dei puntatori a funzioni. Il byte di input viene passato a una struttura di macchina a stati (potrebbe essere semplice come una custodia) che passa a vari bit di codice in funzione della variabile o delle variabili di stato. Inoltre, non sei del tutto chiaro da dove provenga il tuo stdin (non che contenga davvero molto, ma sono curioso).
Adam Lawrence,

9
@EJP Non sono d'accordo. Solo perché c'è una sovrapposizione non significa che deve essere su un sito o sull'altro. Si tratta di porre domande relative alla progettazione e alla programmazione di sistemi embedded di basso livello, che sembrano in tema in entrambi i casi.
Kortuk,

Le macchine a stati possono essere basate sull'indirizzamento delle funzioni: la chiamata alla funzione di transizione di stato restituisce una nuova funzione di transizione di stato.
Kaz,

1
Diamo questa discussione qui .

Risposte:


21

Una funzione

int f(int x){ .. }

Ottieni l'indirizzo di una funzione (e memorizzalo in una variabile)

int (*fp)(int) = f;

Chiama una funzione a un indirizzo specificato

int x = (*fp)( 12 );

3
+1, ovvio nel mondo C e ASM, ma non così evidente per coloro che hanno iniziato con le lingue OO.
Anindo Ghosh,

4
La chiamata fp può anche essere scritta come 'int x = fp (12);' per aumentare la leggibilità.
Rev1.0

1
Devo ammettere che, lavorando principalmente in C # e Java attualmente, a volte mi mancano quei buoni vecchi puntatori di funzioni di C. Sono pericolosi se non eseguiti esattamente nel modo giusto e la maggior parte dei compiti può essere eseguita in altri modi, ma sono sicuramente utili quelle poche volte in cui sono davvero utili.
un CVn

@ MichaelKjörling: così vero. Passo costantemente dalla programmazione C e C # incorporata (anche implementando un codice simile per la comunicazione tra dispositivo e PC) e per quanto potente possa essere C #, mi piace ancora tanto C.
Rev1.0

2
fp(12)non aumenta la leggibilità (*fp)(12). Hanno una leggibilità diversa. (*fp)(12)ricorda al lettore che fpè un puntatore e ricorda anche la dichiarazione di fp. Nella mia mente, questo stile è associato a vecchie fonti, come gli interni di X11 e K&R C. Un possibile motivo per cui potrebbe essere associato a K&R C è perché in ANSI C è possibile dichiarare fpusando la sintassi della funzione, se fpè un parametro: int func(int fp(double)) {}. In K&R C sarebbe stato int func(fp) int (*fp)(double); { ... }.
Kaz,

17

Mentre la risposta di Wouters è assolutamente corretta, questo forse è più adatto ai principianti e un esempio migliore per quanto riguarda la tua domanda.

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

Se non è ovvio che al puntatore a funzione sia effettivamente assegnato un indirizzo di funzione target, è consigliabile eseguire un controllo NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */

8
+1 per il controllo null, ma si noti che il compilatore potrebbe non garantire che il valore all'indirizzo nella RAM che si fptrova ad occupare sia effettivamente NULL inizialmente. Vorrei fare int (*fp)(int) = NULL;come una dichiarazione combinata e l'inizializzazione per essere certi - non vuoi saltare in una posizione di memoria casuale a causa di un valore sciocco che è appena successo. Quindi basta assegnare fpcome nella tua Example()funzione.
un CVn

1
Grazie per il commento, ho aggiunto l'inizializzazione NULL.
Rev1.0

4
@ MichaelKjörling buon punto, ma se fpè una "variabile globale" (come nel codice sopra), allora è garantito che sia inizializzato NULL(senza doverlo fare esplicitamente).
Alok
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.