Witam wszystkich,jak widać jestem zupełnie nowy na forum Atnel, choć nie zupełnie nowy w świecie AVR i C, dość często tu zaglądałem ale nie miałem jakoś czasu aby zostać na dłużej.
Mam nadzieję, że teraz uda mi się zaglądać częściej i częściej coś napisać, dziś chciałbym zacząć z niewielkim poradnikiem dotyczącym zabawy ze znakami oraz stringami na naszych procesorkach AVR pokazując prostą metodę szyfrowania tekstu.
Może to być pewne uzupełnienie informacji które pojawiły się podczas konkursu Mirka:
http://mirekk36.blogspot.com/2013/08/am ... ajaca.htmlTak wiec zaczynajmy
1. Wstęp Każdy myślę pamięta, że stałe znakowe w języku C przechowywane są za pomocą 7-bitowych kodów ASCII (choć zajmują oczywiście 8 bitów), są więc po prostu liczbami i
tak jak liczby poddają się wszelkim operacjom arytmetycznym.
Pozostaje więc odpowiedzieć sobie na pytanie do czego mogą nam takie operacje być potrzebne?
Polecam rzucić okiem na tablicę wszystkich kodów ASCII np. tutaj:
http://pl.wikipedia.org/wiki/ASCII widząc to w takiej formie łatwiej jest wszystko zrozumieć i można od razu zauważyć, że znaki ułożone są w dość logiczny sposób, np. co dla nas jest dość ważne, cyfry, małe i wielkie litery znajdują się w spójnych blokach oraz są prawidłowo posortowane (cyfry rosnąco, litery alfabetycznie)
tak np. cyfry od 0 do 9 zajmują kody ASCII od 30 do 39 i już tu możemy zauważyć pewne zastosowanie:
każdy znak reprezentujący liczbę 0-9 możemy niemal natychmiast zamienić na jego wartość dziesiętną odejmując od wartości kodu ASCII liczbę 30 (czyli nasze '0')
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Spoglądając na układ wielkich oraz małych liter w tabeli kodów ASCII, powinniśmy np. łatwo dostrzec, że długość alfabetu możemy policzyć operacją 'z' - 'a' + 1 lub też: 'Z' - 'A' + 1, dodatkowo biorąc pod uwagę, że odległość małych liter od ich wielkich "odpowiedników" jest stała i wynosi: 'a' - 'A' (ale też oczywiście 'c' - 'C' lub 'z' - 'Z') możemy np. napisać funkcje zamieniającą nam wszystkie wielkie litery w tekście na małe:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Krótkie przypomnienie dotyczące C-stringów:
W wyniku zapisu: char *str = "Hello"; otrzymujemy tak w rzeczywistości wskaźnik na literę 'H' w naszym "Hello". Aby teraz odwołać się np. do litery 'e' możemy skorzystać z dwóch równoznacznych zapisów:
*(str+1) lub str[1] ale możemy też po prostu przesunąć nasz wskaźnik (na stałe) na literę e przesuwając go na następny bajt pamięci a więc dodając do aktualnej wartości jedynkę, tak więc po operacji str++ nasz str wskazywać już będzie na literę e i to na tej właśnie zasadzie działa powyższa funkcja
Wykorzystajmy więc te informacje przejdźmy do napisania prostej funkcji szyfrującej
.
2. Szyfr przesuwający - szyfrowanie. Jest to najprostszy rodzaj szyfru podstawieniowego - czyli takiego w którym litery szyfrowanego wyrazu podmieniamy na inne litery bądź w ogóle znaki.
Szyfr przesuwający jak sama nazwa wskazuje, zastępuje wszystkie szyfrowane litery, inną, oddaloną o stałą liczbę pozycji literą (liczbę pozycji ustalamy sami i pozostaje ona stała dla całego szyfrowanego wyrażenia).
Litery które wykroczą poza alfabet "zawijamy" na początek (np. Y przesunięte o 2 to już litera A)
Zasadę działania bardzo dobrze obrazuje obrazek z Wikipedii prezentujący działanie szyfru dla przesunięcia 3 czyli tzn. Szyfru Cezara.
Spróbuję teraz pokazać jak łatwo możemy taki szyfr zaimplementować w języku C, na nasze potrzeby przyjmiemy, że wielkie litery wprowadzonego wyrażenia zamienimy na małe oraz damy też możliwość przesuwania się o ujemną liczbę pozycji (czyli w lewo).
Zacznijmy od pewnych spostrzeżeń: liczbę liter w alfabecie znamy ( jest ich 'z' - 'a' +1
), łatwo też zauważyć, że przesuwając się o taką liczbę wrócimy do naszej pierwotnej litery - szyfr dla przesunięcia 0, 26 ale też 52, 78 itd. pozostawi wiadomość szyfrowaną bez zmian a przesunięcie o liczbę 27 w rzeczywistości spowoduje przesunięcie tylko o 1, zgadza się?
Kolejną rzeczą jaką trzeba zauważyć jest to, że np. przesunięcie o liczbę 2 w lewo jest równoznaczne z przesunięciem o 26 - 2 = 24 pozycje w prawo i tak analogicznie każde przesunięcie o ujemną liczbę pozycji (czyli w lewo) możemy zamienić na odpowiadające mu przesunięcie w prawo.
Gdy to już jest jasne, możemy zacząć od nagłówka naszej funcji, załóżmy, że będzie on miał następującą postać:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
czyli funkcja będzie zwracać zaszyfrowaną wiadomość w nowym stringu.
Wiemy, już, że na początku możemy wykonać takie operacje:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Jako, że nasza funkcji zwraca nam odszyfrowaną wiadomość będziemy potrzebować miejsca w pamięci aby ją umieścić, załatwimy to najłatwiej tworząc kopię oryginalnej wiadomości którą później zmodyfikujemy i zwrócimy:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
utworzyliśmy też dodatkowy wskaźnik o którym powiem na końcu, na razie ustawmy go na aktualną zwracaną wiadomość :>
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Po takich przygotowaniach, możemy przejść do głównej części:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
na koniec wystarczy nam już tylko zwrócić zaszyfrowaną wiadomość ale jest tu pewien haczyk, otóż nasz wskaźnik zaszyfrowana_wiadomosc w każdym obrocie pętli był przesuwany o jeden znak do przodu i w tym miejscu wskazuje już na znak końca stringu - zwracanie go nie ma więc najmniejszego sensu.
Tutaj z pomocą przyjdzie nam wskaźnik tmp którego nigdzie nie modyfikowaliśmy więc on nadal wskazuje na początek naszej szyfrowanej wiadomości, wystarczy więc zakończyć naszą funkcję w taki sposób:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Oczywiście całą sprawę można było rozwiązać na wiele różnych sposobów i ominąć tego typu zabiegi, wystarczyło np. użyć pętli for i zapisu zaszyfrowana_wiadomosc[i] ale specjalnie chciałem pokazać coś innego
3. Odszyfrowywanie. Odszyfrowanie wiadomości to nic innego jak wykonanie operacji odwrotnej tzn. przesunięcia wszystkich liter o jednakową ilość pozycji tyle, że w drugą stronę. My, jako, że trochę bardziej rozbudowaliśmy funkcję szyfrującą, robić już nic nie musimy, nasza funkcja odszyfrowująca będzie miała po prostu postać:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
I tak oto dobrnęliśmy do końca tej powtórki z operacji znakowych w C
Pozostaje mi poruszyć jeszcze tylko jedną kwestię, do wydzielenia sobie kawałka pamięci na nasz zwracany string użyliśmy funkcji strdup która jak łatwo się domyślić dokonuje alokacji pamięci - a skoro tak to powinniśmy też tą pamięć zwalniać (szczególnie biorąc pod uwagę, że nasze procesorki ramem nie grzeszą
)
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Poniżej prezentuję jeszcze kod pierwszej funkcji złożony razem:
język c
Musisz się zalogować, aby zobaczyć kod źródłowy. Tylko zalogowani użytkownicy mogą widzieć kod.
Jeśli chcielibyście w przyszłości usłyszeć coś konkretnego to proszę podrzucić propozycję tutoriala do języka C, C# lub Javy (ale tylko Android)
PS. To mój pierwszy poradnik w życiu, proszę o wyrozumiałość
Pozdrawiam,
Dzedor