Programowanie
Programowanie
tiriśigned; jnt
typ zmiennej
wypisywana jest, dziesiętne, liczba że znakiem identyczny do tczba bez znaku,; dziesiętnie iczba bez znaku, szesnastkowo, wykorzystane
bez znaku, szesnastkowo. wykorzystane są duże
bez znaku, ósemkowo
,, isanie wskaźnika jako adresu w pamięci w formacie 0x1234
wypisanie znaku podanego jako argument char*. wypisanie łańcucha z pamięci danych
prog_char* wypisanie łańcucha z pamięci programu _
liczba zmiennoprzecinkowa w formacie naukowym 1.23e±12
liczba zmiennoprzecinkowa w formacie naukowym 1.23ŁŁ12
liczba zmiennoprzecinkowa w formacie dziesiętnym 123.45
identyczny do f
jeśli eksponenta z notacji naukowej jest mniejsza niż -4, lub większa / równa precyzji, wykonywane jest przekształcenie równoważne że; w innym przypadku ; double wykorzystane jest |
podobnie do g, tylko wykonywane są przekształcenia E
Tabela 6 - oznaczenia typów w formatowania
Tabela 7 - flagi w łańcuchu formatowania
# |
dotyczy |
opis bez efektu |
x lub X |
zawsze z zerem przed całą liczbą vypisuje 0x przed liczbą | |
• |
wszystkie |
wyrównanie długości odbywa się jrzez dodanie zer zamiast spacji wyrównanie do lewej, spacje są |
[spacja] |
numeryczne *3 numeryczne |
eśli liczba jest dodatnia, w miejscu gdzie powinien znajdować się znak + wprowadzana jest spacja; ma znaczenie dla wyrównania -domyślnie żaden znak nie zostałby wprowadzony eśli liczba jest dodatnia pojawia się przed nią znak |
naksymalną
naków, jak
wypisa
Składnia funkcji printf wygląda następująco: int printf(const char fmt,...)
Widoczne trzy kropki to nie żadne Wprowadzone przeze mnie uproszczenie. To dość ciekawa oraz całkowicie formalna cecha języka C. Trzy kropki pojawiają się zawsze za ostatnim argumentem. Nie można za nimi umieścić jeszcze kolejnego, ustalonego argumentu. Format, z jakim mamy tułaj do czynienia oznacza, że zaraz za parametrem fmt możemy umieścić dowolną ilość dowolnych parametrów.
Pierwszym parametr nazywany jest z angielska „Format String”, co można tłumaczyć jako łańcuch formatujący. W nim zawarte są informacje, jakie parametry zostały wprowadzone i z jego pomocą są one odzyskiwane. Sposób podawania odpowiednich informacji pojawi się za chwilę, teraz jednak skupmy się na reszcie argumentów.
Każdy kolejny argument, wpisany w miejsce trzech kropek, jest umieszczany na stosie. Jednak wrzucane są one „od tyłu”. W efekcie, w chwili wywołania funkcji, pierwszy argument znajduje się na samym szczycie stosu i może być jako pierwszy z niego zdjęty. Zmienne zdejmowane będą od najmłodszego bąjtu do najstarszego.
W ten sposób można do funkcji przekazać argumenty w objętości ograniczonej jedynie pojemnością pamięci przeznaczonej na stos. Jest to niezwykle wygodne w funkcji takiej jak printf, gdzie nie da się określić, jakie argumenty będą potrzebne, jednak z możliwości tej trzeba korzystać bardzo ostrożnie...
Ryzyko i wady rozwiązania
Osoby obeznane trochę z programowaniem zdają sobie sprawę z tego, jak ważnym elementem jest stos. Jakikolwiek błąd w przepływie informacji między stosem a rejestrami może skutkować zawieszeniem się całego programu. Trzeba zdać sobie sprawę, że o ile w przypadku zwyczajnych funkcji, z ustalonymi parametrami, to kompilator dba o prawidłową obsługę stosu, nie jest on w stanie pilnować jego działania, gdy korzystamy z otwartej listy argumentów. Gdy decydujemy się na takie działanie, to na nas spoczywa odpowiedzialność za prawidłowe jego funkcjonowanie. Kompilator nic będzie także w' stanie sprawdzić, czy podawane argumenty są właściwego typu. Bardzo ważne jest więc, aby podane argumenty zgadzały się z tym, co deklarujemy w łańcuchu formatowania. W innym przypadku funkcja printf może nie zdjąć wszystkich informacji zc stosu. Może także zdjąć ich zbyt wiele. Błąd stosu może spowodować, że dalej, do rejestrów wpisane zostaną przypadkowe dane. W skrajnym przypadku śmieci zostaną wprowadzone do licznika rozkazów... rozumiesz na pewno, co się wtedy stanie.
Oprócz koniecznej ostrożności trzeba zwrócić uwagę na jeszcze jedną sprawę. Jeśli wywoływana funkcja posiada jedynie stałe argumenty, w miarę możliwości są one umieszczane bezpośrednio w rejestrach. Odpowiedni podprogram w
dokładnie typy wszystkich argumentów - ma je podane niejako „na tacy”. Jeśli natomiast wywołujemy funkcję
wszystkie one muszą zostać umieszczone na stosie. Wymaga to dodatkowego czasu oraz kodu przed wywołaniem podprogramu. Sam podprogram także musi być bardziej rozbudowany.
Ogólnie, jeśli to możliwe, funkcji tego typu raczej się unika. Czasami jednak jest to albo jedyne... albo najlepsze rozwiązanie - mimo wszystkich wad.
w naszej funkcji. Teraz dowiemy się, jak ją wykorzystać. Bardzo ważny jest łańcuch formatujący. TO w nim zawarta jest informacja o przekazanych parametrach oraz o sposobie, w jaki zostaną one wyświetlone. Znaki z łańcucha formatującego są wysyłane bezpośrednio na wyjście do chwili natrafienia na znak Znaki występujące za nim są interpretowane jako informacja o kolejnym argumencie.
Rysunek powyżej pokazuje format opisu argumentu oraz sposobu jego wyświetlenia.
Z naszego punktu widzenia, najważniejsza jest informacja o typie argumentu. Oznaczenia poszczególnych typów przedstawia tabela 6. Nie sugeruj się zbyt mocno podanymi tam typami zmiennych. Najważniejsze jest zachowanie ich rozmiaru. Pod tym względem int oraz unsigned int są identyczne.
Przydatne flagi zamieściłem w tabeli 7.
Nasz kompilator udostępnia tylko jeden modyfikator: ‘I’ oznaczający, że zamiast zmiennej typu int podajemy zmienną typu long int.
Znaczenie precyzji oraz szerokości wyjaśnia dodatkowa, niewielka ramka.
printf - pola precyzja oraz szerokość
Znaczenie pola precyzji zmienia się zależnie od typu. Dla typów zmiennoprzecinkowych oznacza ono liczbę miejsc po przecinku. Dla iypów numerycznych minimalną liczbę cyfr (dopełnienie zerami, niczalcż-
Jeśli długość napisu tworzonego prze? kształcenie jest mniejsza niż szerokość, dodawane są spacje, względnie zera (flaga ‘0’) po lewej lub prawej (flaga 1 stronie napisu. Parametr len nigdy nie powoduje przycięcia wyniku.
jakie efekty da podanie określonej szerokości lub precyzji. Wypróbuj różne flagi. Spróbuj skorzystać także z typów ‘s’ oraz ‘S\
Ponieważ jest to dość ciekawe,
Rys. 40 Działanie programu z listingu 57
proponuję Ci zapoznanie się z rysunkiem 41. który pokazuje ostatni kod uruchomiony za pomocą programu AVRStudio. Spróbuj samemu przeprowadzić pokazaną na nim symulację. Widzimy tutaj, w jaki sposób poszczególne argumenty są przesyłane do funkcji. Widzimy, że jest to jak najbardziej zgodne z opisem w ramce o naszej funkcji. Zauważ, że w tym przypadku także parametr określający adres łańcucha formatowania przesyłany jest przez stos. Informacja o tym znajduje się w dokumentacji dołączonej do WinAVR... nie ma dla nas tak dużego znaczenia przesyłanie, nieznanej nam wcześniej, otwartej listy argumentów.
Jeśli w którymś momencie zadałeś sobie pytanie, dlaczego ciągle umieszczam łańcuchy w pamięci danych, zamiast jak już pokazywaliśmy
Elektronika dla Wszystkich Luty2006 45