Krawi92 Napisano Grudzień 27, 2021 Udostępnij Napisano Grudzień 27, 2021 (edytowany) Cytat Więc zamiast tego spróbuj je przećwiczyć. Powiedzmy że masz za zadanie zrobić pięć funkcji które nie pobierają ani nie zwracają żadnej wartości. Zbuduj tablicę wskaźników na te funkcje. Jak byś to zrobił? @_LM_ W nawiązaniu do twojego pytania, spróbuję odpowiedzieć. Nie wiem czy miałeś do na myśli. Oto pseudokod. #include <avr/io.h> void migaj_LED1(void); void migaj_LED2(void); void migaj_LED3(void); void migaj_LED4(void); void migaj_LED5(void); typedef void (*Twsk) (void); // nowy typ wskaznika na funkcje typu void Twsk wskaznik[5]; // Stworzenie tablicy 5 elementowej typu Twsk enum {led1,led2,led3,led4,led5}; int main () { // przypisanie wskazników do funkcji wskaznik[led1] = migaj_LED1; wskaznik[led2] = migaj_LED2; wskaznik[led3] = migaj_LED3; wskaznik[led4] = migaj_LED4; wskaznik[led5] = migaj_LED5; } void migaj_LED1(void){ // migamy led } void migaj_LED2(void){ // migamy led } void migaj_LED3(void){ // migamy led } void migaj_LED4(void){ // migamy led } void migaj_LED5(void){ // migamy led } Edytowano Grudzień 27, 2021 przez Krawi92 Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 Podłączyłem sobie kilka led, żeby nie patrzeć tylko na kompilacje. 5 funkcji miga 5 roznymi diodami i do funkcji odwołuje się rozumiem przez wskaźnik. wskaznik[led1](); W ten sposób jak np: w pętli while wywołam funkcję to jest ok Link do komentarza Share on other sites More sharing options...
ethanak Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 I dokładnie o to chodzi! 1 Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 Teraz pytanie może nie na miejscu, bo wiem że powinienem sam gdzieś czytać o tym itp. Po co takie odwoływanie się za pomocą wskaźnika do funkcji ? Rozumiem, że na takim pseudokodzie ciężko to zrozumieć, bo na razie nie widzę tu sensu. Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
H1M4W4R1 Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 5 minut temu, Krawi92 napisał: Teraz pytanie może nie na miejscu, bo wiem że powinienem sam gdzieś czytać o tym itp. Po co takie odwoływanie się za pomocą wskaźnika do funkcji ? Rozumiem, że na takim pseudokodzie ciężko to zrozumieć, bo na razie nie widzę tu sensu. #ifndef MENULIB_ACTION_H #define MENULIB_ACTION_H #include "actions/i_action_metadata.h" class action_t; // Callback definition typedef void (*callback_t)(action_t* action, i_action_metadata* metadata); // Action types typedef enum { ON_PRESS = 1, ON_RELEASE = 2, ON_HOLD = 4, ON_DECREASE = 8, ON_INCREASE = 16, ON_CHANGE = (ON_DECREASE | ON_INCREASE) } action_type_t; class action_t { private: callback_t callback; action_type_t actionType; public: action_t(callback_t callback, action_type_t actionType); void execute(i_action_metadata* metadata); action_type_t getActionType(); }; #endif //MENULIB_ACTION_H By zrobić coś takiego: #define MENU_INCREASE_DECREASE_OPTION(title, listener_increase, listener_decrease) (new menu_option_t(title))->add_listener(new action_t(listener_increase, action_type_t::ON_INCREASE))->add_listener(new action_t(listener_decrease, action_type_t::ON_DECREASE)) W skrócie klasa action_t opisuje zdarzenie występujące po wprowadzeniu danych do urządzenia (np. zmiany wartości potencjometru czy wciśnięcia przycisku). Dzięki temu mogę mieć wiele akcji, które wykonują się w momencie, w którym np. wartość potencjometru wzrośnie. Potem mogę je wywołać wszystkie w bardzo prosty sposób: menu_common_t::execute(ON_INCREASE, nullptr); W skrócie wskaźników do funkcji używasz wtedy, gdy chcesz przypisać funkcję do jakiejś zmiennej i przechowywać ją w obiekcie skonstruowanym tak, że pozwala użytkownikowi dodać funkcję do wykonania bez ingerencji w strukturę obiektu. Brzmi skomplikowanie... W skrócie - jak chcesz mieć możliwość przypisania funkcji, której jeszcze nie masz, a potem nie będziesz mógł edytować tego kodu, bo będzie to np. biblioteka. Możesz sobie pokopać tutaj by znaleźć przykład użycia: https://github.com/H1M4W4R1/MCU_Menu_Library (tak to ten sam kod). Link do komentarza Share on other sites More sharing options...
ethanak Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 Pewnie na razie się nie przyda. Ale za jakiś czas kiedy będziesz robić coś bardziej skomplikowanego niż miganie ledami będziesz już znał jedną całkiem fajną metodę. Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 No właśnie widzę, kolega @H1M4W4R1 pokazuje na przykładzie C++, gdzie obiekty,klasy itp nie są mi znane i ciężko mi to przeanalizować. Poczekam na @_LM_ może jeszcze kilka przykładów wymyśli, żeby coś przećwiczyć 😉 Link do komentarza Share on other sites More sharing options...
_LM_ Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 A to jest dosyć proste: powiedzmy że chcesz funkcje migaj wywołać w zależności od jakiejś zmiennej, bez wskaźników zrobiłbyś to na switch case: #include <avr/io.h> void migaj_LED1(void); void migaj_LED2(void); void migaj_LED3(void); void migaj_LED4(void); void migaj_LED5(void); int main () { uint8_t licznik; for(;;){ switch(licznik){ case 0:migaj_LED1(); break; case 1:migaj_LED2(); break; case 2:migaj_LED3(); break; case 3:migaj_LED4(); break; case 4:migaj_LED5(); break; default:break; } } } } void migaj_LED1(void){ // migamy led } void migaj_LED2(void){ // migamy led } void migaj_LED3(void){ // migamy led } void migaj_LED4(void){ // migamy led } void migaj_LED5(void){ // migamy led } Zaś mając tablicę wskaźników na te funkcje zrobisz to w jednej linii #include <avr/io.h> void migaj_LED1(void); void migaj_LED2(void); void migaj_LED3(void); void migaj_LED4(void); void migaj_LED5(void); typedef void (*Twsk) (void); // nowy typ wskaznika na funkcje typu void Twsk wskaznik[5]; // Stworzenie tablicy 5 elementowej typu Twsk enum {led1,led2,led3,led4,led5}; int main () { // przypisanie wskazników do funkcji wskaznik[led1] = migaj_LED1; wskaznik[led2] = migaj_LED2; wskaznik[led3] = migaj_LED3; wskaznik[led4] = migaj_LED4; wskaznik[led5] = migaj_LED5; uint8_t licznik; for(;;){ if(wskaznik[licznik])wskaznik[licznik](); // sprawdzanie czy ptr przypisany, jesli tak to wykonaj } } void migaj_LED1(void){ // migamy led } void migaj_LED2(void){ // migamy led } void migaj_LED3(void){ // migamy led } void migaj_LED4(void){ // migamy led } void migaj_LED5(void){ // migamy led } 1 Link do komentarza Share on other sites More sharing options...
_LM_ Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 (edytowany) Podstawą korzystania z wskaźników na funkcje jak i inne obiekty jest sprawdzenie czy on na coś wskazuje. Dlatego że odwołanie się do wskaźnika na NULL lub nieokreślone miejsce w programie, doprowadzi w najlepszym wypadku do resetu mikrokontrolera, w gorszym do nieoczekiwanej podmiany danych w pamięci SRAM stąd ta instrukcja w main if(wskaznik[licznik])wskaznik[licznik](); Edytowano Grudzień 27, 2021 przez _LM_ 1 Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 No troszeczkę się rozjaśniło, właśnie zastanawiał mnie ten warunek, ale juz wytłumaczyłeś. Link do komentarza Share on other sites More sharing options...
Elvis Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 @Krawi92 Zastosowań wskaźników do funkcji jest całkiem sporo. Bardzo prosty, wręcz książkowy przykład to funkcja sortująca - powiedzmy coś takiego: void swap(int *xp, int *yp) { int temp = *xp; *xp = *yp; *yp = temp; } // A function to implement bubble sort void bubbleSort(int arr[], int n) { int i, j; for (i = 0; i < n-1; i++) // Last i elements are already in place for (j = 0; j < n-i-1; j++) if (arr[j] > arr[j+1]) swap(&arr[j], &arr[j+1]); } W takim kodzie masz na stałe zaszyte porównywanie elementów, czyli (arr[j] > arr[j+1]). Gdybyś chciał posortować elementy powiedzmy malejąco, zamiast rosnąco, musiałbyś napisać nową funkcję. Jedną z metod napisania ogólnego kodu jest przekazanie jeszcze jednego parametru do bubbleSort(), czyli funkcji porównującej. Użycie jej zamiast > sprawi, że można będzie ten sam kod używać do różnych sortowań. Jako ćwiczenie możesz spróbować taki kod napisać. Kolejne zastosowania wskaźników funkcji to funkcje zwrotne (callback). Często wykonanie jakiegoś kodu zajmuje sporo czasu, a na koniec trzeba o tym powiadomić inny fragment programu. Przekazanie wskaźnika do funkcji to dość dobre rozwiązanie. Bardzo ważnym zastosowaniem jest również programowanie obiektowe w C. Wbrew często powtarzanej opinii jest to jak najbardziej możliwe oraz często stosowane. Wskaźniki funkcji zapisane w strukturach pozwalają na uzyskanie równie dobrych, a nawet lepszych rezultatów niż z użyciem C++. 2 Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 @_LM_ kod, który wrzucałem w poście o tworzeniu menu widzę, że będę dzięki tablicy wskazników skrócić. Tam użyłem właśnie switcha i nacisniecie przycisku zwiekszalo licznik i wywoływało odpowiednią funkcje. Link do komentarza Share on other sites More sharing options...
_LM_ Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 (edytowany) Podam jeszcze jeden prosty przykład: powiedzmy że z uart przylatuje jakiś znak i na jego podstawie masz wykonać akcję -> wywołać funkcję przypisaną do tego znaku. Można to zrobić w taki sposób że odgórnie połączymy spodziewane znaki z adresami funkcji które mają się wykonać #include <avr/io.h> #define MAXPTR 5 void migaj_LED1(void); void migaj_LED2(void); void migaj_LED3(void); void migaj_LED4(void); void migaj_LED5(void); typedef void (*Twsk) (void); // nowy typ wskaznika na funkcje typu void typedef struct{ Twsk f; char c; }io_t; io_t fun[] = { {.c = 'A',.f = migaj_LED1}, {.c = 'D',.f = migaj_LED2}, {.c = 'E',.f = migaj_LED3}, {.c = 'x',.f = migaj_LED4}, {.c = 'q',.f = migaj_LED5} }; int main(void){ char cl = uart_getc(); for(;;){ for(uint8_t i = 0; i<MAXPTR; i++){ if(cl == fun[i].c){ // sprawdzamy co przyleciało z uart jesli znak znajduje sie w tablicy to nast krok if(fun[i].f){ fun[i].f(); //sprawdzenie i wywolanie funkcji break; // dalej nie trzeba szukac } } } } } void migaj_LED1(void){ // migamy led } void migaj_LED2(void){ // migamy led } void migaj_LED3(void){ // migamy led } void migaj_LED4(void){ // migamy led } void migaj_LED5(void){ // migamy led } Edytowano Grudzień 27, 2021 przez _LM_ 1 Link do komentarza Share on other sites More sharing options...
_LM_ Grudzień 27, 2021 Udostępnij Grudzień 27, 2021 Oczywiście kod należałoby okrasić niezbędnymi dodatkami typu że po wywołaniu funkcja wykona się tylko raz itd.. Link do komentarza Share on other sites More sharing options...
Krawi92 Grudzień 27, 2021 Autor tematu Udostępnij Grudzień 27, 2021 (edytowany) Wrócę to kawałka kodu, gdzie za pomocą przycisku skakałem to funkcji. #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "lcd.h" #include "twi.h" uint8_t licznik; int main(){ microInit(); i2cSetBitrate(100); sei(); lcd_init(); lcd_cls(); czas.YY = 2000; // set time //save_timedata(&time); // INT0 interrupt EICRA |= (1<<ISC01); EIMSK |= (1<<INT0); PORTD |= (1<<PD2); while(1){ if (!keylock1 && !(PIND & (1<<KEY1))){ keylock1=1; licznik++; if (licznik > 3)licznik=0; }else if (keylock1 && (PIND & (1<<KEY1)))keylock1++; switch(licznik){ case 0:RTC_Event(); break; case 1:display_menu2(&czas); break; case 2:display_menu(); break; case 3:zapisano_czas(); break; } } } Rozumiem, ze mogę to zrobić tak. #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "lcd.h" #include "twi.h" uint8_t licznik; typedef void (*Twsk)(void); // nowy typ wskaznika na funkcje Twsk wskTab[4]; // tablica wskazników na funkcje enum{wyswietl_czas,ustaw_date,ustaw_czas,zapisz_dane}; // typ wyliczeniowy dla czytelnosci int main(){ // przypisanie wskaznikow do funkcji wskTab[wyswietl_czas]=RTC_Event; wskTab[ustaw_date]=(Twsk)display_menu2; // Jawne rzutowanie bo jedna funkcja przyjmowała wskTab[ustaw_czas]=display_menu; //jako argument wskaźnik do struktury Ttime. wskTab[zapisz_dane]=zapisano_czas; microInit(); i2cSetBitrate(100); lcd_init(); lcd_cls(); EICRA |= (1<<ISC01); EIMSK |= (1<<INT0); PORTD |= (1<<PD2); czas.YY = 2000; sei(); while(1){ if (!keylock1 && !(PIND & (1<<KEY1))){ keylock1=1; licznik++; if (licznik > 3)licznik=0; }else if (keylock1 && (PIND & (1<<KEY1)))keylock1++; if(wskTab[licznik])wskTab[licznik](); // jesli przypisano cos do ptr, wywolaj ptr na funkcje. } } Edytowano Grudzień 27, 2021 przez Krawi92 Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »