Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2013 mgr
Jerzy Wałaszek
|
"Ruch uliczny"
Na poprzednich zajęciach używaliśmy stałych łańcuchowych do inicjalizacji tablic. Musisz tutaj dokładnie zrozumieć różnicę pomiędzy inicjalizacją tablicy, a operatorem przypisania. Inicjalizacja określa zawartość tablicy w momencie jej tworzenia. Dla tablic znakowych stosujemy następującą konstrukcję:
char nazwa_tablicy[] = "dowolny tekst";
Zwróć uwagę, że nie określamy liczby elementów tablicy. Kompilator sam to zrobi na podstawie dostarczonego tekstu. Rozmiar tablicy będzie taki, aby pomieścić wszystkie znaki tekstu oraz kończący je znak NUL.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_021 //----------------------------------- #include <iostream> using namespace std; int main() { char s[] = "Ruch uliczny"; // inicjalizacja tablicy cout << s << endl; // użycie tablicy return 0; } |
Tablicę można również inicjować ciągiem pojedynczych znaków:
char nazwa_tablicy[] = {znak,znak,...,0};
W tym przypadku samemu należy zatroszczyć się o umieszczenie na końcu znaku NUL, jeśli tablica ma być w programie traktowana jako cstring.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_022 //----------------------------------- #include <iostream> using namespace std; int main() { char s[] = {'A','B','C',0}; // inicjalizacja tablicy cout << s << endl; // użycie tablicy return 0; } |
W powyższym programie usuń z definicji końcowe 0 i sprawdź, co się wtedy stanie po uruchomieniu. Wytłumacz takie zachowanie programu.
Tak możemy inicjować tablicę. Lecz w C++ niedopuszczalna jest konstrukcja:
tablica_znakowa = "tekst";
Sprawdź, czy uda ci się uruchomić poniższy program:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_023 //----------------------------------- #include <iostream> using namespace std; int main() { char s[100]; // tworzenie tablicy 100 znaków s = "Ruch uliczny"; // ??? cout << s << endl; // użycie tablicy return 0; } |
Dlaczego powstaje błąd w wierszu:
s = "Ruch uliczny";
Błąd powstaje dlatego, iż C++ nie wie, w jaki sposób ma wykonać tę operację. Mamy tutaj do czynienia z operacją złożoną – w tablicy s należy umieścić znaki tworzące tekst. To wymaga pętli oraz sprawdzania różnych warunków:
Dlatego C++ pozostawia programiście decyzję, jak tę operację należy przeprowadzić (w dalszej części kursu poznamy klasę string, która zawiera odpowiednie metody do wykonywania tego typu działań, na razie w celach dydaktycznych pozostaniemy przez pewien czas przy tablicach znakowych, aby poznać ich zalety i wady).
Stała tekstowa "Ruch uliczny" w programie jest traktowana jako adres, wskaźnik stały. Tekst stałej jest umieszczany w pamięci programu, a wartością stałej staje się adres tego obszaru – bardziej konkretnie jest to wskaźnik stały do danych typu char (czyli typ wskaźnikowy char *). Aby się o tym przekonać, wystarczy ten adres zrzutować na adres do danych typu void i przesłać go w tej postaci do strumienia cout. Teraz strumień cout nie zinterpretuje tego adresu jako cstring, lecz jako zwykły wskaźnik i wypisze szesnastkowo jego wartość.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_024 //----------------------------------- #include <iostream> using namespace std; int main() { cout << (void *) "Ruch uliczny" << endl; return 0; } |
Teraz rozumiesz, że komputer nie wie, jak ma przypisać tablicy wskaźnik do danych typu char. Co innego, jeśli zamiast tablicy s użyjemy wskaźnika p:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_025 //----------------------------------- #include <iostream> using namespace std; int main() { char * p; // wskaźnik do danych char p = "Ruch uliczny"; // w p umieszczamy adres tekstu cout << p << endl; return 0; } |
Możesz się początkowo dziwić, że wskaźnik p i tablica znakowa s są traktowane przez strumień cout w identyczny sposób. Nie ma w tym żadnej tajemnicy. Wskaźniki i tablice są w C++ równoważne. Czym właściwie jest tablica? Obszarem pamięci, który przechowuje jej elementy. Nazwa tablicy odwołuje się do adresu tego obszaru, czyli tablica jest wskaźnikiem do typu danych, który posiadają jej elementy. Różnica jest tylko taka, iż adres tablicy jest stały, nie można go zmienić. Natomiast zmienna typu wskaźnik może przyjmować różne adresy. Poniższy przykład pokazuje równoważność wskaźnika i tablicy:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_026 //----------------------------------- #include <iostream> using namespace std; int main() { char s[] = "Ruch uliczny"; // tablica char * p; // wskaźnik int i; p = s; // p wskazuje elementy tablicy s for(i = 0; s[i]; i++) cout << p[i] // wskaźnik jako tablica << " = " << *(s + i) // tablica jako wskaźnik << endl; return 0; } |
Przy przypisaniu adresu wskaźnikowi nie ma miejsca przesyłanie danych ze
wskazywanego przez ten adres obszaru. Taką operację C++ może zawsze
bezpiecznie wykonać. Co jednak zrobić, jeśli faktycznie chcemy umieścić w
tablicy zadany tekst (stałą tekstową lub zawartość innej
tablicy). Rozwiązaniem jest funkcja StrCpy() kopiująca tekst z
jednego obszaru pamięci do drugiego.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_027 //----------------------------------- #include <iostream> using namespace std; // Funkcja kopiuje tekst z obszaru s do d //---------------------------------------- char * StrCpy(char * d, const char * s) { char * p = d; // ustawiamy p na początek obszaru d while((*p++ = *s++)); // kopiujemy znaki z s do d, aż do napotkania NUL return d; // zwracamy adres d } int main() { char s[100],t[100]; char * p; StrCpy(s,"Ruch uliczny"); p = StrCpy(t,s); cout << "s = " << s << endl << "t = " << t << endl << "p = " << p << endl; return 0; } |
Często musimy znać liczbę znaków zawartych w łańcuchu. Operację tę wykonuje funkcja StrLen():
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_028 //----------------------------------- #include <iostream> using namespace std; // Funkcja oblicza liczbę znaków w tekście //---------------------------------------- int StrLen(const char * s) { int n = 0; // zerujemy licznik znaków while(*s++) n++; // zliczamy znaki w s return n; // zwracamy licznik } int main() { char s[100]; cout << "Wpisz wiersz : "; cin.getline(s,100); cout << "Liczba znak\242w w tek\230cie jest r\242wna " << StrLen(s) << endl; return 0; } |
Kolejną pożyteczną funkcją jest łączenie tekstów, czyli konkatenacja. Polega to na tym, iż na koniec tekstu docelowego d jest dołączany tekst źródłowy s. Oba parametry są wskaźnikami do char. Obszar wskazywany przez d powinien być wystarczająco duży, aby pomieścić oba teksty po ich połączeniu.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_029 //----------------------------------- #include <iostream> using namespace std; // Funkcja dołącza do d tekst s. Zwraca d //--------------------------------------- char * StrCat(char * d, const char * s) { char * p = d; // p ustawiamy na początek obszaru d while(*p) p++; // szukamy końca łańcucha w d while((*p++ = *s++)); // kopiujemy znaki z s na koniec tekstu w d return d; // zwracamy adres początku obszaru d } int main() { char s[100]; char t[] = " A nam nic do tego!"; s[0] = 0; // teraz s zawiera pusty łańcuch StrCat(s,"Ala ma "); // do s wstawiamy tekst StrCat(s,"\276\242\210wika Bzika."); // dodajemy tekst StrCat(s,t); // dodajemy tekst z t cout << s << endl; return 0; } |
W ten sposób można sobie napisać wszystkie potrzebne funkcje. Jednakże programiści C++ (a właściwie C) zrobili to już dawno temu i definicje funkcji dla łańcuchów cstring umieścili w pliku nagłówkowym cstring. Trzy napisane przez nas funkcje: StrCpy(), StrLen() i StrCat() są tam obecne w prawie identycznej postaci (nazwy funkcji bibliotecznych pisane są małymi literami). Z funkcji bibliotecznych opłaca się korzystać, ponieważ zostały one dokładnie przetestowane i są stabilne w działaniu. Poniżej umieszczamy opis najczęściej używanych funkcji bibliotecznych wraz z krótkimi programami. Opisy reszty funkcji z łatwością znajdziesz w sieci Internet.
char * strcpy(char * d, const char * s);
d | – | wskaźnik obszaru przeznaczenia, gdzie zostanie skopiowany łańcuch wskazywany przez s. Obszar wskazywany przez d powinien być wystarczająco duży na pomieszczenie znaków z obszaru s wraz z końcowym znakiem NUL. Obszary d i s nie powinny się pokrywać w pamięci. |
s | – | wskaźnik obszaru źródłowego, z którego będzie pobierany łańcuch cstring. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_030 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[100],t[100]; strcpy(s,"Pierwszy tekst"); cout << "s = " << s << endl; strcpy(s,"Drugi tekst"); cout << "s = " << s << endl; strcpy(t,s); cout << "t = " << t << endl; return 0; } |
int strlen (const char * s);
s | – | wskaźnik obszaru przechowującego łańcuch cstring. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_031 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char n[100]; cout << "Wpisz swoje imi\251 : "; cin >> n; cout << "Liczba znak\242w twojego imienia = " << strlen(n) << endl; return 0; } |
char * strcat(char * d, const char * s);
d | – | wskaźnik obszaru przechowującego łańcuch cstring, do którego zostanie dołączony łańcuch wskazywany przez s. |
s | – | wskaźnik obszaru przechowującego dołączany łańcuch. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_032 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[100]; char s1[] = "Kocie "; char s2[] = "m\210ocie, "; char s3[] = "co siedzisz "; char s4[] = "na p\210ocie."; strcpy(s,s1); strcat(s,s2); strcat(s,s3); cout << strcat(s,s4) << endl; return 0; } |
const char * strchr( const char * s, int c);
char * strchr(
char * s, int c);
s | – | wskaźnik obszaru przechowującego łańcuch cstring, który zostanie przeszukany. |
c | – | kod znaku, który będzie wyszukiwany w łańcuchu s. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_033 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[255]; char * p; int a = 0; // licznik literek a cout << "Wpisz wiersz : "; cin.getline(s,255); p = s; // p wskazuje początek łańcucha s while((p = strchr(p,'a'))) // szukamy litery a { a++; // zwiększamy licznik p++; // wskaźnik przesuwamy na następny znak w s } cout << "Liczba literek a w wierszu = " << a << endl; return 0; } |
const char * strstr(const char * s1, const char * s2);
char * strstr( char
* s1, const char * s2);
s1 | – | adres łańcucha do przeszukania. |
s2 | – | adres łańcucha, który będzie poszukiwany w s1. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_034 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[255]; char * p; cin.getline(s,255); p = s; while((p = strstr(p,"al"))) { *p++ = 'A'; *p++ = 'L'; } cout << s << endl; return 0; } |
void * memset(void * d, int v, int n);
d | – | adres wypełnianego obszaru. |
v | – | dane do wypełnienia, traktowane jako char. |
n | – | liczba bajtów do wypełnienia w obszarze d. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_035 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[1000]; memset(s,'A',999); s[999] = 0; cout << s << endl; return 0; } |
void * memcpy(void * d, const void * s, int n);
d | – | adres obszaru, do którego będą skopiowane dane. |
s | – | adres obszaru, z którego będą kopiowane dane. |
n | – | liczba bajtów do skopiowania |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_036 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[100]; char t[] = "OWA"; char * p = s; cin.getline(s,100); while((p = strstr(s,"owa"))) { memcpy(p,t,3); p += 3; } cout << s << endl; return 0; } |
void * memmove(void * d, const void * s, int n);
d | – | adres obszaru, do którego będą skopiowane dane. |
s | – | adres obszaru, z którego będą kopiowane dane. |
n | – | liczba bajtów do skopiowania |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Łańcuchy znakowe // PRG_037 //----------------------------------- #include <iostream> #include <cstring> using namespace std; int main() { char s[100]; strcpy(s,"abra cadabra "); memmove(s + 5,s,strlen(s)+1); cout << s << endl; return 0; } |
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe