Jak to robi GCC
Co to są tak naprawdę zmienne lokalne?
Kiedy zdefiniujesz jakąś zmienną lokalną, zostanie ona umieszczona w jednym lub większej liczbie z 32 rejestrów obliczeniowych mikrokontrolera. Gdy rejestrów jest za mało lub tworzona jest zmienna typu tablicowego - zostanie ona umieszczona na wierzchołku stosu. Jeśli to konieczne, wartość wykorzystywanego rejestru zostanie wcześniej zapisana na stosie i odtworzona z chwilą gdy przestaniesz używać zmiennej. GCC wykorzystuje rejestry bardzo inteligentnie. Jeśli na przykład kompilator „zauważy”, że jedna zmienna lokalna nie jest już używana, natomiast zaczynamy używać innej, może ona skorzystać z tego samego rejestru.
Gdy dana funkcja się kończy, rejestry zostaną zwykle zamazane inną wartością. To wyjaśnia wszelkie ewentualne wątpliwości na temat, czy nie da się jakąś sztuczką dostać z zewnątrz do zmiennych lokalnych funkcji...
zmiennej typu eolatile.
ifCg_bDe1ay > 0) —g_bDelay;
Zmienne lokalne
Do tej pory wykorzystywaliśmy zmienne globalne. Jak pamiętasz, tworzyło się je na samym początku pliku programu - poza jakimkolwiek ciałem funkcji. Często zdarza się jednak, że jakiejś zmiennej potrzebujemy tylko na chwilę i tylko w jednej funkcji. Do takich celów zostały stworzone właśnie zmienne lokalne.
Zmienną lokalną tworzy się dokładnie tak samo jak zmienną globalną. Możemy w tym celu korzystać ze wszystkich dostępnych typów. Różnica polega na miejscu deklaracji. Zmienną lokalną tworzymy zaraz po otwarciu klamry. Jeszcze zanim pojawi się jakakolwiek instrukcja.
GCC umożliwia stworzenie zmiennej lokalnej w dowolnym miejscu bloku*, nie tylko na jego początku. Jest to właściwość zaczerpnięta z języka C++. Jej wykorzystanie jest wygodne, jednak trzeba liczyć się z mniejszą przenośnością kodu.
Inicjacja
Jeśli nic nadano wartości zmiennej lokalnej, jej wartość początkowa jest nieokreślona (przypadkowa). Jest to istotna różnica w stosunku do zmiennych globalnych, które w takim przypadku są automatycznie zerowane przy starcie programu.
Do generowania opóźnień wykorzystywana jest zmienna gbDelay. Sztuczka polega na tym, że jeśli znajduje się w niej wartość różna od 0, jest ona zmniejszana w przerwaniu obsługi wyświetlacza. Zmniejszanie odbywa się z częstotliwością 122Hz. Aby uzystać na przykład opóźnienie równe jednej sekundzie, należy wpisać do g_bDelay wartość 122, a następnie odczekać, aż jej wartość spadnie do 0.
W punkcie O odbywa się deklaracja wspomnianej zmiennej. Informację co oznacza pojawiające się tutaj słówko volati!e, znajdziesz w jednej zc wspomnianych na początku ramek. Zastanowienie może budzić sposób obsługi wspomnianej zmiennej w punkcie © . Wydawać by się mogło, że dostęp, realizowany jak to zostało przedstawione na listingu 16, będzie wymagał mniej działań zc strony procesora. Jednak w przypadku zmiennej typu volatile nicjest to prawdą. Związane jest to z tym, że zabraniamy kompilatorowi optymalizacji dostępu do zmiennej g_bDeiay. W przypadku jak na listingu 16, w pierwszej linii wartość zmiennej g_hDelay zostanie wczytana z pamięci do rejestru, następnie będzie porównana z wartością 0. Jednak już w drugiej linii kompilator musi założyć, że w tym czasie jej wartość mogła się zmienić. Tak więc przed zmniejszeniem jej wartości zostanie ona ponownie wczytana.
Zmienna, która zostanie utworzona w danym bloku*, będzie dostępna tylko i wyłącznie w jego wnętrzu. Uważaj tutaj na nazwy. N ie jest błędem stworzenie zmiennej lokalnej o nazwie takiej samej jak, na przykład, zmienna globalna. Jednak uniemożliwi do dostęp do „wyższej” zmiennej. Między innymi dlatego zaleca się stosowanie przedrostka g_ przed zmiennymi globalnymi.
Ponieważ zmienna lokalna będzie przechowywana cały czas w rejestrze, przy zastosowaniu sztuczki jak na listingu 15, oszczędzimy jednego, zbędnego nam wczytania z pamięci RAM. Nie jest to wiele, jednak warto pamiętać o tej sztuczce, jeśli będziemy chcieli wykonać bardziej rozbudowany ciąg działań na zmiennej tego typu, w czasie którego nie interesuje nas, czy wartość zmiennej ulegnie zmianie albo nawet mamy pewność, żc w tym miejscu zmiana taka jest niemożliwa.
Jeśli chcesz zobaczyć, jak zachowują się zmienne lokalne, spróbuj zmienić kod pętli głównej na przedstawiony w obrazku w tej ramce. Zobacz, co się stanie, jeśli „czerwoną" zmienną u przeniesiesz przed instrukcję for.
* Blokiem nazywamy część kodu objętą klamrami.
W celu obsługi klawiatury, na początku kodu do definicji wyprowadzeń zostały dodane linie nadające symboliczne oznaczenia wyprowadzeniom przycisków. Podczas inicjacji włączane jest wewnętrzne podciąganie wejść przełączników do dodatniego bieguna zasilania. Zauważ, żc nie ma tutaj jawnej konfiguracji jako wejścia odpowiednich wyprowadzeń portu. W tym przypadku możemy oprzeć się na prostej zasadzie, że i tak wszystko, czego nie zdefiniujemy jako wyjście, będzie wejściem.
Miejsce na obsługę klawiatury znalazło się w pętli głównej. Znajdziesz tutaj dwie symetryczne części: każdą służącą do obsługi innego klawisza. Ze względu na symetrię omówię tylko jedną z nich. Przydatne informacje na
40 Wrzesień2005 Elektronika dla Wszystkich