Znamy już przyczynę problemu. Jak go rozwiązać? W posiadanej przez nas wersji
Jak to robi GCC Dlaczego fdevopen jest tak pamięciochłonna?
Dokumentacja funkcji fdevopcn informuje nas, żc tunkcja ta tworzy nową strukturę FILE, alokując w tym celu dynamicznie pamięć. Daje nam to dość ciekawą możliwość: gdy zaczynamy używać na przykład interfejsu RS232, dokonując powiązania poprzez funkcję fdevnpcn, zajmujemy pamięć. Gdy teraz zamkniemy połączenie za pomocą funkcji felose - pamięć zostanie zwolniona. Jest
Dynamiczna alokacja pamięci danych wymaga umieszczenia dość skomplikowanych procedur w pamięci programu. Czy to się opłaca? Zauważyłeś może, że do tej pory nie zapoznałem Cię z funkcją felose? Prawda jest taka, żc w mikrokontrolerze bardzo rzadko mamy do czynienia z sytuacją, gdy chcemy zamknąć otwarty strumień.
W ogromnej większości przypadków to po prostu nic ma sensu. Zwykle dokonamy powiązania wszystkich strumieni w chwili startu programu i nie będziemy ich nigdy zwalniać. Wykorzystanie dynamicznego przydzielania pamięci jest więc dla nas przerostem formy nad treścią.
biblioteki stdio nie ma innej niż fdevopen funkcji umożliwiającej powiązanie strumienia ze zmienną FILE. Rozwiązaniem byłoby samodzielne utworzenie zmiennej typu FILE w normalny, poznany przez nas do tej pory sposób - bez dynamicznego zajmowania pamięci. Następnie musielibyśmy samodzicl-
Ustmg 60 - wykorzystanie funkcjifprintf
int mainfyoid)
FILE* IRS; int a = 1234; int b = Oxff;
// inicjacja portu szeregowego
// Koniec inicjacji
H Inicjacja we/wy
£‘SS = fdevopen(rs_put, rs_get, 0);
farintf P(fRS i PSTO( „a(%%d)=%d\r\n"
„a(»Sx)=%x\r\n"
„a(%%x)=%x\r\n‘‘
„b(%%#x)=9ś#x\r\n"
„b(%%o)=Sto\r\n"),
nie wpisać do niej odpowiednie wartości. Nie jest to eleganckie rozwiązanie... jest ponadto (pozornie) niemożliwe.
Zmienna typu FILE jest definiowana w ramach omawianej biblioteki jako struktura. Zapoznaj się z ramką opisującą korzystanie z tych użytecznych tworów. Trochę przydatnych informacji o tym, jak używać struktury jako nowego typu danych, znajdziesz w ramce ABC... C typedef- nazywanie typu. Zajrzyj do niej. Niedługo będziemy korzystać z opisanej tam możliwości.
Początek naszej idei pozbycia się problemu pokazuje listing 61, będący przeróbką programu z listingu 59. Chwilowo nie wpisujemy nic do struktury (jeszcze nic wiemy, jakie posiada składowe). Ważne jest jedynie sprawdzenie, jak zareaguje kompilator. Okaże się, że już na tym poziomie pojawią się problemy. Spróbuj skompilować taki program. Jeśli wszystko zrobiłeś prawidłowo, to kompilacja pójdzie... źle. Otrzymasz komunikat o błędzie polegającym na tym, że nie jest znany rozmiar struktury gJRS. Cóż to za dziwna struktura, której rozmiar jest nieokreś-
{
pPt-:
Sytuacja bardzo zbliżona do poprzed- memc niej, korzystamy jedynie z innego operatora.
GO prawda możliwe jest działanie bardziej „zakręco-
(«p?t).X = 0 ;
Przypomnę, żc za pomocą operatora gwiazdki dostajemy się do obiektu wskazywanego przez wskaźnik. Możemy korzystać z tego jak powyżej, ale jest to mniej eleganckie j mniej czytelne niż podanyj wcześniej sposób.
Przedstawioną funkcję wywołamy jak niżej;
; Zeruj(4punktA) _
Przykład może nie jest zaawansowany, ale pokazuje już siłę, jaka drzemie w strukturach. Zauważ, że do funkcji Zeruj przekazujemy tak naprawdę tylko jeden argument - 16-bitowy adres a w jej wnętrzu mamy dostęp do wszystkich pól naszej struktury.
Nadanie wartości początkowych
Nadanie wartości początkowych strukturze odbywa się podobnie jak miało to miejsce w przypadku tablic. W nawiasach klamrowych musimy podać wartości
Wspomnialem o tym, że zmienna typu FILE to struktura. Struktura to bardzo wygodny obiekt, w którym możemy pogrupować wszystkie potrzebne nam zmienne jakiegoś typu. Wyobraźmy sobie, że chcemy utworzyć strukturę zawierającą informację o punkcie: struct [punkt]
int x; int y; int z;
} [punkLA, punktB, x];
Pola umieszczone w nawiasach kwadratowych są opcjonalne. Zaraz zą słowem kluczowym struct występuje etykieta. Jeśli umieścimy ją tutaj, będziemy mogli wykorzystać ją zamiast wszystkiego, co zawarliśmy w nawiasach'klamrowych. Przykładowo, chcemy utworzyć kolejny punkt:
I struct punkt Kolejny;
Z kolei wszystko, co ż.ostanie umieszczone za nawiasem klamrowym, powoduje utworzenie odpowiednich zmiennych. W praktyce składnia:
Struct {...} zmienna;
Oznacza to samo co:
; int zmienna;
Z tym że w pierwszym przypadku tworzymy obiekt typu podanej struktury, w drugim tworzymy obiekt typu int.
W nawiasach klamrowych umieszczamy informacje o składowych struktury. Po prostu wymieniamy kolejno występujące w niej zmienne oraz nadajemy im nazwy. Możemy korzystać tutaj z wszelkich dostępnych typów zmiennych. Wcześniej zdeklarowane struktury także mogą być wykorzystane.
Należy pilnować jedynie, aby nazwy składowych nie pokrywały się między sobą. Jednak nazwa struktury może przyjąć postać identyczną jak na przykład etykieta czy nazwa zmiennej globalnej - nie ma obawy o błędy w programie.
Dostęp do odpowiedniej zmiennej w strukturze jest rozróżniany poprzez kontekst.
Dostęp do składowych, wykorzystanie w programie
Do składowych Struktury możemy się dostać na dwa sposoby. Pierwszy z nich dotyczy przypadku, gdy dysponujemy odpowiednią zmienną. W naszym przypadku rueclj będzie to pfmltlA. Do poszczególnych pól Struktury dostaniemy się za pomocą operatora kropki: punkt A. x = 0; punktA.y = 1;
punktA.z = 2;
Myślę, że nie budzi to wątpliwości. Jeśli teraz wydaje się nienaturalne, stanie się prostsze podczas pisania przykładów.
Drugi sposób dotyczy momentu, gdy mamy dostęp do wskaźnika na strukturę. Można wyobrazić sobie następujący przykład:
31 d Zeruj (struct punkt *pPt)
< = 0;
każdej składowej. Pamiętaj, że takie działanie dozwolone jest jedynie w chwili tworzenia zmiennej: struct punkt z = {0, 1, 2};
Operowanie całymi strukturami
Jeśli stworzysz własną strukturę, możesz przekonać się, że nic działają na. nich standardowe operatory. Zapis jak poniżej jest nieprawidłowy: puntA = punktB;
Strukturę należy traktować jako pewien ciąg danych w pamięci (obrazek w ramce). Sprawa jest zbliżona do działania tablic. Mamy dwie możliwości zastąpienia powyższego przepisania. Jedna polega na przepisywaniu wszystkich pól, dla których powyższe działanie jest dozwolone: punktA.x = punktB.x; punktA.y = punktB.y; punktA.z = punktB.z;
Poła x, y, z są typu int. Ożycie operatora przypisania na zmiennych typu int jest możliwe.
Druga, wygodniejsza i często bardziej optymalna metoda polega na przekopiowaniu bloku pamięci: :py(&punktA, &punktB, SIzeof (punktA)) ;
Funkcja memepy skopiuje dane z obszaru wskazywanego przez drugi argument do obszaru wskazywanego przez pierwszy argument Trzeci argument podaje w bajtach ilość danych do skopiowania. Operator sizeof zostanie w czasie kompilacji zamieniony na odpowiednią wartość.
j Każda składowa ; typu int zajmie
Wskaźnik na zmiennąpsn&Ł4
38 Marzec 2006 Elektronika dla Wszystkich