Projektowanie kodowania C - wskaźniki funkcji?

24

Mam PIC18F46K22 i programuję go za pomocą kompilatora XC8. Na koniec będę mieć system podobny do komputera z stdini stdout. Tak więc w głównej pętli pojawi się funkcja sprawdzająca, czy jest nowe wejście. Jeśli jest wejście, funkcja zostanie odpowiednio wywołana. Na przykład, kiedy wprowadzę A stdin, PIC uruchomi funkcję podobną function_Ado tej, function_Bktóra jest wywoływana, gdy wprowadzę B.

Kiedy PIC jest wykonywany z funkcją, chcę, aby nowe wejście zostało wysłane do funkcji. Tak więc po naciśnięciu A otwiera się nadajnik RS232, od tego momentu każde wejście będzie wysyłane przez RS232. Ostatecznie projekt jest samodzielnym edytorem tekstu. Kiedy więc naciśniesz A, otwiera system plików, od tej chwili nie edytujesz już tekstu, ale przeglądasz listę plików. Oznacza to, że naciśnięcie przycisku w górę lub w dół oznacza coś innego niż w środowisku do edycji tekstu.

Dużo zastanawiałem się, jak zaprogramować to w C. Myślałem o tym wczoraj i chciałbym wiedzieć, czy to możliwe, a jeśli tak, to w jaki sposób. Chcę zrobić:

  • mainFunkcja wywołuje funkcję jakfunction_A
  • function_Azmienia zmienną globalną function_addrna wskaźnik adresu funkcjiin_function_A
  • Od tego momentu mainwywołuje funkcję w function_addrmomencie pojawienia się nowego wejścia.

Potrzebuję więc mainfunkcji sprawdzającej, czy function_addrwynosi zero. Jeśli tak, należy wywołać funkcję „normalną”, np function_A. Jeśli nie jest, function_addrnależy wywołać funkcję at . Potrzebuję również, function_Aktóry zmienia function_addrwskaźnik na in_function_A.

Uwaga: kiedy funkcja systemu plików powinna być zamknięta, is_function_Apowinna zmienić się function_addrna 0.

Więc w zasadzie moje pytanie brzmi: jak mogę

  • Uzyskaj adres funkcji (i zapisz go w zmiennej)
  • Wywołaj funkcję pod określonym adresem

źródło
6
Poza tematem. Należy do stackoverflow.com.
user207421,
Dla mnie podejście oparte na maszynie stanów jest znacznie mniej ryzykowne niż radzenie sobie ze wskaźnikami funkcji. Twój bajt wejściowy jest przekazywany do struktury maszyny stanów (może być tak prosta jak skrzynka przełączników), która przeskakuje do różnych bitów kodu w zależności od zmiennej stanu lub zmiennych stanu. Ponadto, nie jesteś całkowicie pewien, skąd pochodzi twoje standardowe wejście (nie to, że naprawdę ma to duże znaczenie, ale jestem ciekawy).
Adam Lawrence
9
@EJP Nie zgadzam się. To, że nakładają się na siebie, nie oznacza, że ​​musi znajdować się na jednej lub drugiej stronie. Zadaje pytania związane z projektowaniem i programowaniem systemów wbudowanych niskiego poziomu, które wydają się być tematem w obu miejscach.
Kortuk
Automaty stanów mogą być oparte na pośredniej funkcji: wywołanie funkcji zmiany stanu zwraca nową funkcję zmiany stanu.
Kaz
1
Porozmawiajmy tutaj .

Odpowiedzi:

21

Funkcja

int f(int x){ .. }

Uzyskaj adres funkcji (i zapisz go w zmiennej)

int (*fp)(int) = f;

Wywołaj funkcję pod określonym adresem

int x = (*fp)( 12 );
Wouter van Ooijen
źródło
3
+1, oczywiste w świecie C i ASM, ale nie tak oczywiste dla tych, którzy zaczynali od języków OO.
Anindo Ghosh,
4
Wywołanie fp można również zapisać jako „int x = fp (12);” w celu zwiększenia czytelności.
Rev 1.0
1
Muszę przyznać, że obecnie pracuję głównie w C # i Javie, czasami tęsknię za tymi starymi dobrymi wskaźnikami funkcji z C. Są niebezpieczne, jeśli nie są właściwie wykonane, a większość zadań można wykonać na inne sposoby, ale z pewnością są przydatne w tych kilku przypadkach, gdy naprawdę się przydają.
CVn
@ MichaelKjörling: Tak prawda. Ciągle przełączam się między wbudowanym programowaniem w C i C # (nawet implementuję podobny kod do komunikacji między urządzeniem a komputerem) i jakkolwiek może być C #, nadal cieszę się C tak bardzo.
Rev1.0
2
fp(12)nie zwiększa czytelności (*fp)(12). Mają różną czytelność. (*fp)(12)przypomina czytelnikowi, że fpjest wskaźnikiem, a także przypomina deklarację fp. Moim zdaniem ten styl jest powiązany ze starymi źródłami, takimi jak elementy wewnętrzne X11 i K&R C. Jednym z możliwych powodów, dla którego można go skojarzyć z K&R C, jest to, że w ANSI C można zadeklarować fpużycie składni funkcji, jeśli fpjest parametrem: int func(int fp(double)) {}. W K&R C byłoby to możliwe int func(fp) int (*fp)(double); { ... }.
Kaz
17

Chociaż odpowiedź Woutersa jest absolutnie poprawna, być może jest to bardziej przyjazny dla początkujących i lepszy przykład dotyczący twojego pytania.

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

Jeśli nie jest oczywiste, że wskaźnik funkcji faktycznie ma przypisany adres funkcji docelowej, dobrze jest sprawdzić wartość NULL.

if (fp != NULL)
    fp(); /* with parameters, if applicable */
Rev1.0
źródło
8
+1 dla kontroli zerowej, ale zwróć uwagę, że kompilator może nie gwarantować, że wartość adresu w pamięci RAM, która fpsię zajmuje, początkowo jest równa NULL. Zrobiłbym to int (*fp)(int) = NULL;jako połączoną deklarację i inicjalizację, aby być pewnym - nie chcesz przeskakiwać do jakiejś przypadkowej lokalizacji w pamięci z powodu jakiejś głupiej wartości, która właśnie się tam pojawiła. Następnie po prostu przypisz fpjak w swojej Example()funkcji.
CVn
1
Dzięki za komentarz, dodałem inicjalizację NULL.
Rev1.01,
4
@ MichaelKjörling ma rację, ale jeśli fpjest to „zmienna globalna” (jak w powyższym kodzie), to gwarantuje się, że zostanie ona zainicjowana NULL(bez konieczności jawnego).
Alok,