Writing in C or C++ is like running a chain saw with all the safety guards removed
Do ukończenia tego laboratorium wymagana jest znajomość wskaźników i referencji z C. Jeżeli ktoś nie czuje się w tym temacie na siłach, w laboratorium Wskaźniki i referencje przygotowane zostało krótkie przypomnienie najważniejszych informacji.
Często zdarza się, że nie jesteśmy w stanie z góry przewidzieć ile pamięci będzie potrzebował nasz program do poprawnego działania. Na przykład w prostym kalkulatorze macierzowym nie można z góry ustalić wymiarów tablicy przechowującej macierz ani z góry ustalić ile takich tablic-macierzy będzie występować w równaniu!
Jedynym rozwiązaniem jest tworzenie nowych macierzy o ustalonych wymiarach podczas działania programu.
Wraz z dynamiczną alokacją pamięci idą w parze wskaźniki. Pamięć zawsze alokowana jest pod jakimś adresem. A zmienne przechowujące adres i pozwalające się dostać do danych w niej przechowywanych, to właśnie wskaźniki.
Operator new jest ekwiwalentem znanej z C funkcji malloc ale dużo potężniejszym. Wynikiem zwracanym przez operator new jest wskaźnik do zaalokowanej pamięci
Przykład dynamicznego tworzenia zmiennych:
#include <iostream> using namespace std; int main(void){ char* ptr = new char; //Stworzy zmienną typu char int* tab = new int[10]; //Stworzy tablicę typu int o rozmiarze 10 // NIEPOPRAWNIE!!! int** tab2d = new int[10][10]; }
Operator delete działa podobnie jak funkcja free znana z C. Pamięć w momencie kiedy nie jest już wykorzystywana przez program powinna być zwolniona. Przykład dynamicznego tworzenia i usuwania zmiennych:
#include <iostream> using namespace std; int main(void){ char* ptr = new char; //Stworzy zmienną typu char int* tab = new int[10]; //Stworzy tablicę typu int o rozmiarze 10 // Tutaj znajdują się operacje na zmiennych delete ptr; delete [] tab; // NIEPOPRAWNIE!! // Jeśli tab2d jest poprawnie zaalokowaną tablica dwuwymiarową // nie można zwolnić pamięci w następujący sposób delete [][] tab2d; }
Operacje na plikach wykonywane są w C++ przy użyciu strumieni - analogicznie jak operacja czytania z klawiatury i wypisywania na ekran.
Należy pamiętać o tym, że każdy plik, który został otwarty, powinien zostać również zamknięty, kiedy korzystanie z niego nie jest już potrzebne.
Obiekty strumieni służące do odczytu i zapisu do pliku znajdują się w pliku nagłówkowym <fstream>. Biblioteka ta zawiera dwa obiekty strumieni analogiczne do obiektów cin i cout:
Przykład wczytywania pliku wyraz po wyrazie:
#include <iostream> #include <fstream> using namespace std; int main(void){ ifstream myfile("file.txt"); char word[64]; if(!myfile) cout << "Nie można otworzyć pliku!" << endl; while(myfile >> word) cout << word; myfile.close(); return 0; }
Przykład wczytywania pliku linia po linii:
#include <iostream> #include <fstream> using namespace std; int main(void){ ifstream myfile("file.txt"); char line[256]; if(!myfile) cout << "Nie można otworzyć pliku!" << endl; while(!myfile.eof()){ myfile.getline(line,256); cout << line; } myfile.close(); return 0; }
Przykład dopisywania do pliku:
#include <iostream> #include <fstream> using namespace std; int main () { // Aby zrozumieć zapis: ios_base::in | ios_base::app // Zobacz kolejny podrozdział ofstream myfile ("file.txt", ios_base::in | ios_base::app); if(!myfile) cout << "Nie można otworzyć pliku!" << endl; myfile << "Tekst zapisany w pliku" << endl; myfile.close(); return 0; }
Plik można otworzyć w 6 różnych trybach; kombinacje trybów też są możliwe.
Wartość flagi | Znaczenie |
---|---|
ios_base::app | (append) Ustawia wskaźnik pozycji strumienia na samym końcu strumienia. Służy do dopisywania do pliku. |
ios_base::ate | (at end) Ustawia wskaźnik pozycji strumienia na samym końcu pliku. |
ios_base::binary | (binary) Traktuje strumień jako strumień binarny a nie tekstowy |
ios_base::in | (input) Umożliwia zapis do pliku |
ios_base::out | (output) Umożliwia odczyt z liku |
ios_base::trunc | (truncate) Jeśli otwierany pli posiadał jakąś zawartość, jest ona pomijana i wskaźnik pozycji w strumieniu jest ustawiany na początku pliku. |
Otwieranie strumienia ifstream z opcją ios_base::out mija się z celem. Do strumieni, które mają służyć zarówno zapisowi jak i odczytowi, stosuje się ogólną postać strumienia o nazwie fstream.
fstream file("file.txt", ios_base::in | ios_base::out | ios_base:app);
./program plik-tekst.txt plik-szyfr.txt 1
A | B | C | D | E | F | G | |
---|---|---|---|---|---|---|---|
A | - | 16 | 12 | 21 | - | - | - |
B | 16 | - | - | 17 | 20 | - | - |
C | 12 | - | - | 28 | - | 31 | - |
D | 21 | 17 | 28 | - | 18 | 19 | 23 |
E | - | 20 | - | 18 | - | - | 11 |
F | - | - | 31 | 19 | - | - | 27 |
G | - | - | - | 23 | 11 | 27 | - |
Zapoznać się z materiałem o Makefile.