Pascal i C - czyli tam i z powrotem- część pierwsza poprawiona
kncik tfórzuff_4p
Jeżeli możesz popraw ten artykuł według zaleceń, które możesz znaleźć na stronie [[Artykuły do poprawy]]. Po dopracowaniu tego tekstu można usunąć ten komunikat.
Autorem jest Flabra, ja (Kapustka) jestem autorem wyglądu i ponieważ uważam, że oryginalny art. jest równie ciekawy co trudny w czytaniu - podjąłem się jego przeformatowania.
Konwencja formatowania jest następująca, kody źródłowe są zamykane w ramkach, natomiast kolor tła związany jest z językiem programowania:
</td></table>
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
C++
</td></table>
C++ i pascal, czyli tam i z powrotem
Spis tresci
- Wstęp
- Semantyka
- Typy zmiennych
- Porównania, warunki, pętle
- Operatory, wyrażenia
- Deklaracje ponownie
- Przeciążania
- Parametry wywołania programu
- Biblioteki/moduły
- Ciekawostki
- Rzeczy przemilczane
1.Wstęp
Nigdzie nie znalazłem podobnej charakterystyki porównawczej... A sądzę, że wielu, którzy znają pascala, chciałoby zacząć pisać w c++ i na odwrót. Wielu ludzi piszących w innych językach uważa C++ za coś dogłębnie związanego z czarną magią... Otóż rzeczywistość jest taka, że jest to tylko częściowo prawdą... Z praktycznego punktu widzenia, człowiek znajacy zasady tworzenia algorytmów jest w stanie nauczyc się każdego języka programowania. W tym artykule chciałem się zająć różnicami pomiędzy językiem pascal i C++. A co za tym idzie również pomiędzy C++ builderem i delphi. Jako że strona ta poświęcona jest głównie C++ i pascalowi niniejszy artykuł też będzie o tym traktował.
Na wstępie nota. W większości przypadków opisy dotyczą wersji dosowych, przeznaczonych do tworzenia programów dla trybu rzeczywistego procesora. A więc Turbo C++ i Turbo/Borland Pascala. Jeżeli dany zapis dotyczy róźnic pomiędzy Delphi i Borland C++ builderem, jest to wyraźnie zaznaczone. Co wcale nie znaczy, że podstawowe zasady nie są prawdziwe.
Chce również uprzedzić, że pisząc to nie chce robić konkurencji Adamowi Boduchowi (I think the most competent of them all - 4programmers), który zresztą to co robi robi bardzo dobrze. To co ja napisałem starajcie się traktować ewentualnie jako uzupełnienie. Moim celem jest tylko i wyłącznie pokazanie, że różnice pomiędzy C/C++ i pascalem nie są takie wielkie i straszne. Że więcej te dwa języki łączy niż dzieli, oczywiście poza firmą produkującą oprogramowanie. Jeżeli poruszam te same tematy w inny sposób, to nie szukajcie dziury w całym, tylko starajcie się wyłapać różne spojrzenia na to samo, aby wzbogacić swoją własną wiedzę.
2.Semantyka (syntaktyka i ogólnie o językach)
a)case sentensive (wielkości liter)Zacznijmy od tego, że w specyfikacja pascala (a również i delphi) dopuszcza mieszanie dużych i małych liter w nazwach własnych, tak więc zapis write będzie równoznaczny z zapisem Write, czy WrItE. Natomiast C++ takiej możliwości nie dopuszcza. Trzeba się pilnować, aby przykładowy printf był zapisany zgodnie z deklaracja, czyli w tym przypadku tylko małymi literami. To samo dotyczy wszelkich typów, zmiennych, etykiet i innych rzeczy, które identyfikuje się poprzez nazwy (literały). Na przykład:
</td></table>
Zostanie skompilowane i wykonane bez żadnej reakcji ze strony
kompilatora. Natomiast:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
const char* A="abrakadabra";
main(){
printf(a);
}
</td></table>
zostanie już pierwszej fazie odrzucone przez kopilator. Jak wygląda kompilacja w pascalu i w c ++? Ano pascal ma jeden zintegrowany kompilator (all in one), natomiast w c++ istnieje kilka faz tworzenia kodu wynikowego. Najpierw zaczyna się od stworzenia uproszczonej wersji waszego programu. Jest to nadal c++, tyle że wszelkie typy złozone rozkładane sa na elementy pierwsze. Potem sprawdza się syntaktyczną poprawność źródła programu (dzięki Dryo - one of them - 4programmers), czyli krótko mówiąc wyszukuje błędów ortograficznych. Potem jeśli wszystko poszło dobrze, to znaczy nie było żadnego błędu, program zostaje przetłumaczony na asemblera. W tym momencie do głosu dochodzi program asemblujący (tasm/masm, etc), on tworzy plik o rozszerzenu obj. Jest to mniej więcej odpowiednik pascalowego pliku tpu. Następnie ten i inne pliki obj zostają zlinkowane (np. przez tlink) do postaci wykonywalnej com lub exe. W pascalu jest jeden kompilator, odpowiedzialny za wszystko. On sprawdza tekst i kompiluje go linijka po linijce, a nie jak w C całościowo przechodząc z fazy do fazy w kilku podejściach. Dlaczego obj jest tylko odpowiednikiem? Ponieważ do pliku obj jest kompilowany tak program główny jak i dodatkowe zbiory procedur, funkcji, zmiennych itepe. Natomiast w pascalu tylko unit (moduł) kompilowany jest do pliku tpu. Pliki obj są łączone razem do plików bibliotecznych lib (od library), natomiast pliki tpu do tpl (tp.library) przynajmniej w modułach pisanych dla dosa.</p>
W praktyce samemu można sobie taki plik zrobić np. ``` copy 1.obj+2.obj 12.lib /b copy 1.tpu+2.tpu 12.tpl /b ``` choć służą do tego specjalne programy, które mają też możliwość wyciągania i kasowania z bibliotek poszczególnych plików.
Pod pewnym względem pascalowy tpu góruje nad obj-tem. By użyc obj w c/c++ trzeba do niego mieć plik nagłówkowy - plik z rozszerzeniem h. W tpu są zapisane zmienne, procedury, funkcje i typy- dokładnie opisane. Dla procedur są zawarte m.in takie informacja jak liczba i rodzaj parametrów, typ zwracany- dla funkcji, typ i rodzaj zmiennych globalnych. Natomiast w obj brakuje opisów, jest to plik tylko z danymi, bez jakichkolwiek informacji o typie zmiennych, parametrów i zawracanych danych. Opisy tych typów są właśnie zawarte w pliku h.
Ale jest coś, co daje obj-otom przewagę... Można je wykorzystać nawet w pascalu. Tymczasem unit spod tp6, nie będzie rozpoznany pod tp7 i na odwrót.
b)definicje typów.Powiedzmy chcemy zadeklarować nowy typ... Dla pascala trzeba utworzyć blok ropoczynający się od słowa type :
type
nowytyp=char;
drugityp=integer;
</td></table>
I w ten sposób uzyskujemy dwa nowe typy danych. Natomiast w C++ każdą deklarację typu poprzedza się słówkiem typedef:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
typedef char nowytyp;
typedef int drugityp;
</td></table>
I też uzyskujemy dwa nowe typy zmiennych. W ten sposób można deklarować wszelkie typy, chociaż definicje enumów (pascalowych typów wyliczeniowych), struktur (pascalowych rekordów), czy klas (odpowiedników pascalowych obiektów) można wykonać troszkę inaczej, ale o tym dalej.</p>
c)bloki
Będzie krótko: W pascalu bloki zaczynaja się od słowa begin, a kończą słowem end. W C++ blok zawarty jest pomiędzy znakiem { i }.
Dodatkowym typem bloku dla pascala jest pętla repeat until. Pomiędzy nimi nie trzeba (choć można) używać begin/end.
d)funkcje i procedury.W pascalu istnieja obie te formacje, natomiast w C++ istnieją tylko funkcje. Funkcja w C++ wcale nie musi zwracać wyniku, albo raczej zwraca wynik pusty, nie mniej jednak jast to funkcja. Na przykład chcemy stworzyc fun zwracającą wartość integer.
</td></table>
lub zapis jednoznaczny:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
fun(){
return 10
}
</td></table>
pascal:
</td></table>
lub zapis jednoznaczny:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
function fun:integer;
begin
result:=10
end;
</td></table>
Tu trzeba wyjaśnić cztery rzeczy:</p>
Po pierwsze: Typ int jest typem domyślnym dla funkcji, więc nic nie trzeba wpisywać, a kompilator i tak to zrozumie... Ten int jest dla was i tylko dla was, żebyście nie zapomnieli, albo żeby ktoś nie znający c++ mógł się domyślić o co chodzi.
Po drugie: W C++ return nie jest odpowiednikiem pascalowego przypisania wartości. Return jest słowem obowiązkowym jeżeli funkcja ma zwrócic wynik. Nie da się go pominąć. Wartośc zwracana może, ale ne musi być w nawiasach. Return jest ponadto rozkazem powrotu z funkcji. Czyli w praktyce w pascalu return trzebaby zapisać następująco:
</td></table>
Ale ten exit pomija się gdy po przypisaniu pozostaje tylko end, czyli własnie domyślne exit. W praktyce wynika to z tego, że C++ ma więcej wspólnego z asemblerem (UUU! Dużo, dużo więcej, czytaliście o kompilacji) niż pascal.</p>
<p>Po trzecie: znak := to w pascalu znak przypisania, tak samo jak = w c/c++</p>
<p>Po czwarte: Przypisanie wartości do nazwy funkcji jest jednoznaczne z przypisaniem wartości do predefiniowanej (długie, mądre, ważne słowo- tzn. nie powinno się jej deklarować) zmiennej result oznaczające tak naprawdę zwrócenie wartości (ale bez wyjścia z funkcji, oczywiście).</p>
<p>Chcemy zadeklarować procedurę proc, która wypisuje rożne rzeczy:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
procedure proc;
begin
write('różne rzeczy')
end;
</td></table>
</td></table>
I tu się wyjaśnia dlaczego w C++ są tylko funkcje otóż typ void jest typem pustym o rozmiarze 0, nie można zadeklarować zmiennej takiego typu, choć można zadeklarować do niego wskaźnik (void* wskaznik), co jest tym
samym, co pascalowy nietypowany pointer (wskaznik:pointer) Ale o deklaracjach zmiennych dalej. Natomiast co jest ważne... W c++ deklaracje funkcji muszą zawierać parę nawiasów (), jest to spóścizna po C, gdzie deklarację funkcji bezparametrowej trzeba było wykonać w ten sposób:
typ_zwracany fun(void){
return wartosc_zwracana
}
I nadal jest to możliwe, tylko nikomu się nie chce tego void pisać. ;-)</p>
<p>Jeżeli chodzi o C++, to dla uproszczenia zapisu funkcje zwracające void będę w dalszej części nazywał procedurami też dla uproszczenia i tez dlatego, że nie chce mi się łamac paluchów.</p>
<a name="parametry"></a><b>e)parametry wywołań funkcji/procedur</b>
<p>Zacznę od przykładu (od nagłówków jednakowych procedur):
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
type pinteger:^integer;
procedure proc(a,b:char;c:integer;var d:char;e:pinteger)
</td></table>
</td></table>
Rozumiecie?</p>
<p>Po pierwsze: <sup>integer to wskaźnik typowany do integer'a. W pascalu nie da się zadeklarować złożenia typów w nagłówku. W deklaracjach nagłówków procedur można uzywać tylko typów wcześniej zdefiniowanych. Głupi ten pascal pod tym względem, ale tak jest i to trzeba sobie przyswoić. To wynika własnie z tego, co wcześniej napisałem, kompilator 'all in one' nie da rady inaczej.</sup>integer to to samo, co int* .</p>
<p>Po drugie: W C++ każda zmienna musi być opisana osobno, zaś w pascalu zmienne tego samego typu można deklarować razem oddzielając tylko przecinkiem. Można, nie trzeba. Można tak jak w C++ :
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
procedure proc(a:char;b:char;c:integer;var d:char;e:pinteger)
</td></table></p>
Po trzecie: W C++ deklaracje oddziela się przecinkiem, a w pascalu średnikiem.
Po czwarte: Referencja w pascalu poprzedzana jest słowem var, a w c++ do typu trzeba dodać znaczek & (and).
Info: Referencja, bardzo mądre słowo oznaczające po prostu tyle, że trzeba tu podstawić nie wartość, a zmienną (literał), jest to jakiś sposób na ewentualne zwracanie wyniku, przez procedure/funkcję. Ewentualne, bo nie trzeba. Natomiast gdy tworzy się referencję (przez var lub &), to nie tworzy się nowej zmiennej. Gdy mamy np.:
</td></table>
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
void proc(char a)
</td></table>
to wtedy program kopiuje na stos (też mądre słowo) wartość zmiennej lub wartości podstawionej, tworząc zmienną a, a przy referencji odwołuje się bezpośrednio do zmiennej podstawionej zmieniając tylko jej nazwę na potrzeby wewnętrzne funkcji/procedury. Nie tworzy kopii i nie zajmuje pamięci (stosu).
</p>Po szóste(czego nie widać):
</td></table>
są to deklaracje równoznaczne z poprzednią. Ale, ponieważ w C++ bardzo ładnie czyta się typ zmiennej od prawej (!!! np. e jest wskaźnikiem do inta/integera, a d jest referencją do chara), to warto te znaczki wpisywać zaraz za typem wiążąc w ten sposób logicznie w jedną całość i nie mieszając z nazwą zmiennej.</p>
<p>A więc, czym jest zmienna a w poniższej deklaracji?
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
Jakis_typ jakas_funkcja(int * &a)
</td></table>
Oczywiście jest to referencja do wskaźnika do inta, czyli po pascalowemu:
</td></table>
(np. tak zwraca pointer procedura getmem(var p:pointer,...))
A w mniej chorym zapisie:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
Jakis_typ jakas_funkcja(int*& a)
</td></table>
f)program główny
program główny w C++ to funkcja main w domyśle zwraca ona wynik integer lub void (żadnego). Natomiast w pascalu jest to blok pomiędzy begin i end. - zakończonym kropką. Jest to jedyny end z kropką w programie. Zapamiętać: koniec i kropka! Jeżeli funckja main zwraca wynik intowy: return wartość, to samo można uzyskać w pascalu poprzez wywołanie halt(wartość). Halt to zatrzymanie programu, a wartość to kod błędu wyjścia z programu (zmienna errorlevel z dosa). Jeżeli funkcja nie zwraca wyniku, to w domysle jest przyjmowane, że zwraca 0, jest to wywołanie halt lub halt(0). Czyli krótko mówiąc: zero błedu, wszysko ok. To samo, gdy funkcja main zwraca wynik i wywoła coś takiego: return 0. Pozatym w c/c++ istnieje funkcja exit, dopiero która tak na prawde jest odpowiednikiem pascalowego halt.
Oczywiście funkcja ta nie zwraca żadnych wartości do programu z dwóch względów:
- void to typ zwracany
- po wywołaniu tej funkcji - brak powrotu do programu ;-)
W c/c++ funkcja main może być gdziekolwiek w programie, natomiast blok programu głównego w pascalu jest zawsze na końcu. Po kończącej kropce kompilator przestaje strawdzać dalej. A wiec jeśli napiszecie coś takiego...
</td></table>
...to kompilator nie zauważy dodatkowego napisu. Wracając do C++... Również warto funkcje main tworzyc na końcu... Dlatego, że inaczej trzebaby forłardować funkcje, czyli deklarować je wcześniej (forłard też istnieje w pascalu) np.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```cpp
int fun(char a); // to jest forłard- zakończony średnikiem
int i;
void main(){
i=fun(4)
}
int fun(char a){ // to jest funkcja
return a+5
}
</td></table>
Forłardowanie (wybaczcie mi to ł- wiecie: zasłałem łóżko ;-) ) to nic innego jak deklarowanie funkcji zanim ona sama się pojawi. Tak aby funkcje/procedury będące ponad nią widziały ją. To tak samo jak w pascalu funkcja/procedura widzi tylko te, które są ponad nią.</p>
g)forłardowanie (forwarding)
Już przykład był, teraz szczegóły... Pascal:
Procedure def;
Var a,b:integer;
Begin
A:=5;
B:=6;
c:=abc(a,b)
End;
Function abc(i,j:integer);
Begin
Abc:=i+j
End;
</td></table>
ALBO jednoznaczne
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
function abc(i,j:integer):integer; forward;
Procedure def;
Var a,b,c:integer;
Begin
A:=5;
B:=6;
c:=abc(a,b)
End;
Function abc;
Begin
Abc:=i+j
End;
</td></table>
c++ :
void def(){
int a,b,c;
a=5;
b=6;
c=abc(a,b)
}
int abc(int i,int j){
return (i+j)
}
</td></table>
LUB jednoznaczne
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
abc(int,int); //lub abc(int,int)- int to domyslny typ zwracany
void def(){
int a,b,c;
a=5;
b=6;
c=abc(a,b)
}
int abc(int i,int j){
return (i+j)
}
</td></table></p>
Po pierwsze: W pascalu deklaruje się parametry wywołań przy deklaracji forwardu, stąd w drugiej wersji brak parametrów wywołań w nagłówku samej funkcji. W praktyce kompilator porównuje obie deklaracje parametrów i kiedy jedna jest rózna od drugiej wyskakuje bląd kompilacji. Więc czasem warto po prostu tylko raz zadeklarować parametry. Podobnie jest w unicie: w sekcji interface są forwardy, a w implemantaion same kody funkcji/procedur. Zróbcie błąd a kompilator odrazu zacznie krzyczeć, że forward jest inny od ... coś tam ;)
Po drugie: W C++ przy deklaracji forwardu nie trzeba deklarowac nazw zmiennych, wystarczą same typy.
Po trzecie: Wiecie już jak deklarować zmienne w funkcjach i procedurach: W pascalu pomiedzy nagłówkiem funkcji/procedury, a początkowym beginem, a w C++... I to jest piękne: gdziekolwiek w obrębie funkcji. Tylko że zmienna musi być najpierw zadeklarowana, a potem uzyta. No i deklaracja dotyczy tylko wnętrza bloku {}.
No dobra, przejdźmy do typów zmiennych...
2.Typy zmiennych
a)podstawoweRóżnice pomiędzy podstawowymi typami wyliczeniowymi (skokowymi) zmiennych dla kompilatorów dosa. Otóż w pascalu podstawowymi typami są char, byte, shortint, integer, word i longint, a w C++ char, short, int, long, które mogą być obciążone dodatkowo modyfikatorami signed, czy unsigned. Mutacji i odmian w C++ jest wiecej, dlatego najpierw zajme się nimi. Otóż modyfikator signed daje tylko tyle, że zakres typu zaczyna się ponizej zera, natomiast unsigned oznacza, że zmienna w ten sposób zmodyfikowanym typem ma wartości równe zeru i od niego większe. Signed/unsigned - ze znakiem/bez znaku - oznaczony/nie oznaczony. W pascalu brak tego rodzaju modyfikatorów. Teraz zajmiemy się samym porównaniem typów. W skrócie przedstawię odpowiedniki... wersja DOS.
```cpp C++ ``` | pascal
```
|
zakres</td></tr>unsigned char
```
</td>byte,char
```
</td>0 do 255 {*}</td></tr>signed char
```
</td>shortint
```
</td>-128 do 127 {*}</td></tr>unsigned short
```
</td>word
```
</td>0 do 65535</td></tr>unsigned int
```
</td>word
```
</td>0 do 65535</td></tr>unsigned
```
</td>word
```
</td>0 do 65535 (**)</td></tr>signed int
```
</td>integer
```
</td>-32768 do 32767</td></tr>int
```
</td>integer
```
</td>-32768 do 32767</td></tr>signed short
```
</td>integer
```
</td>-32768 do 32767</td></tr>signed long
```
</td>longint
```
</td>-dużo do duzo-1 (***)</td></tr>long
```
</td>longint
```
</td>-dużo do dużo-1</td></tr>unsigned long
```
</td>Int64
```
</td>0 do bdużo-1 (****)</td></tr></table>
(*) char... Tja, następna ciekawostka: w C++ można literki poddawać różnym operacjom arytmetycznym. Np. 'a'-'A'=32. To znów wynika z tego, że C ma więcej wspólnego z asemblerem niż pascal. Literki- pojedyncze znaki w pojedynczych cudzysłowach po prostu traktowane są jak liczby. Czyli do zmiennej char można równie dobrze przypisać 'a', jak i 97. To czy jest to literka, czy znak zależy tylko od tego jak ją sami w danym momencie potraktujecie. Nic więcej. W pascalu natomiast char jest tylko dla znaków, a byte tylko dla liczb. Znaki w pascalu też zapisuje się w pojedynczych cudzysłowach lub np.: #97 - jest to zapis stałej znakowej #97='a'.
(**) unsigned... można ją deklarować jako unsigned int, unsigned short, albo samo unsigned...
(***) duzo to 2 do potęgi 31.
(****) bduzo to 2*duzo, czyli 2^32. W Pascalu jest Int64, które ma maksymalną wartość `9223372036854775807`
Dlatego do tej pory w funcjach i procedurach widzieliście tylko typy identyczne w obu językach: char i int/integer. No prawie chary troszkę się róznią, ale nie za dużo. Od tej pory będę uzywał wszelkich możliwych.
b)zmienne typu boolowskiego.
Otóż w pascalu istnieją zmienne tego typu jak boolean (w delphi dodatkowo wordbool, longbool, bool). Zmienne tego typu nie wystepowału w starszych wersjach C++, choć sam bool występuje w C++ Builderze. Czy na pewno? Do pewnego stopnia. Za zmienną typu boolowskiego można było przyjąć zmienną każdego typu skokowego (wyliczeniowego), w szczególnści char, int, czy long.
To jest wstęp, reszta jest przy warunkach, bo boole jednoznacznie kojarzą się z warunkami.
c)wskazniki
To już częściowo było:
void* = pointer
int*= ^integer;
Odwołania:
```delphi
Type pint=^integer;
Var i:pint;
Begin
New(i);
I^:=1;
Dispose(i);
{albo:}
Getmem(i,sizeof(pint))
I^:=1;
Freemem(i,sizeof(pint))
End;
</td></table>
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
main{
int* i=new int;
*i=1;
delete i;
// albo:
i=(int*)malloc(sizeof(int*));
*i=1;
free(i)
}
</td></table>
lub (jednoznaczne)
```cpp
main{
int* i;
i=new int;
*i=1;
delete i;
// ...
i=(int*)malloc(sizeof(int*));
*i=1;
free(i)
}
</td></table>
Ale,ale... wskaźniki w pascalu mają zawsze 4 bajty, natomiast w c++ zależy to od dwóch rzeczy:
albo od modelu pamięci, do którego kompilujemy,
albo/i od tego w jaki sposób zadeklarujemy wskaźnik.</p>
<p>Rozmiar wskaźników w C++ to 2/4 bajty. 2-bajtowe to wskażniki bliskie:</p>
<p>Np.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
void near* a;
</td></table>
near jest modyfiktorem tworzącym 2-bajtowy bliski pointer; takie wskaźniki występują domyślnie w takich modelach pamięci: medium/small/tiny, o ile nie użyje się modyfikatora far.
```cpp
void far* a;
</td></table>
Jest to wskaźnik daleki, 4-bajtowy (taki sam jak w pascalu). Defaultowo w modelach pamięci: compact/large/huge o ile nie uzyje się modyfikatora near. Szczerze mówiąc, dla tych modeli pamięci proponuję używać tylko dalekich wskaźników.</p>
<p>W modelach medium/small/tiny, segment danych ma tylko 64 kb, jest to obojętne, najwyżej zuzżjecie więcej pamięci.</p>
<a name="tab"></a><b>d)tablice</b>
<p>Pascal:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
var a:array[1..20]of integer;
type atype=array[byte]of word;
</td></table>
Tablice w pascalu jak i w c++ nie mogą przekroczyć 64kb. Mniej więcej, bo dokładnie za chwilke sami będziecie mogli
sprawdzić sobie sami.</p>
W pascalu tablica ma swój indeks dolny i górny. W przykładach a ma od 1 do 20, deklaracja typu atype deklaruje tablicę od 0 do 255. Tak, można wsadzić taki typ wyliczeniowy, więcej można wsadzić każdy typ wyliczeniowy, byleby tablica nie przekroczyła 64kb. Pod tym względem pascal bije c++ na głowę. Prawidłowe przykładowe deklaracje (typów i zmiennych):
```delphi
var a:array[boolean]of byte;
</td></table>
tablica ma 2 komórki.
odwołania do tablicy:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
a[false]:=10;
a[true]:=20;
</td></table>
Tablica dwuwymiarowa:
```delphi
var a:array[boolean,char]of integer;
</td></table>
odwołanie:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
a[true,#13]:=-1;
</td></table>
ale można się też do tablicy odwoływać na sposób C++:
```delphi
a[true][#13]:=-1;
</td></table>
można zadeklarować np. typ wyliczeniowy:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
type tydzien=(pn,wt,sr,cz,pt,sb,nd);
</td></table>
i tablicę :
```delphi
type atype=array[tydzien,char,false..true]of shortint;
var
a:array[pn..pt]of word;
b:atype;
a[wt]:=10000;
b[sr,#20,false]:=-10;
</td></table>
albo:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
b[sr][#20][false]:=-10;
</td></table>
I tak dalej, i tak dalej na wszelkie możliwe sposoby.</p>
C++ : Tablice są bardziej ograniczone:
Deklaracja wygląda następująco np.:
```cpp
int a[10];
</td></table>
gdzie a jest tablicą 10-elementową o komórkach typu int. Tablica jest ZAWSZE indeksowana od 0 w góre, czyli nasz przykładowa ma indeksy od 0 do 9.
Za to odwołania do tablicy...
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
a[1]to to samo, co *(a+1) - fajnie, nie?
</td></table>
To dlatego że tworząc tablicę tworzy się wskaźnik do tablicy. Tylko trzeba pamiętać. Pamięć tablicom statycznym przydziela program, on rządzi na stosie, on go zwalnia.</p>
Tablica trójwymiarowa:
```cpp
char b[5][10][20];
</td></table>
przypisanie :
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
b[1][2][3]=0;
*(*(*(b+1)+2)+3)=0; // to samo
</td></table></p>
Tablice tworzone dynamicznie:
W c++ wskaźnik do danego typu, jest również wskaźnikiem do tablicy danego typu.
Pascal:
```delphi
Type arrtyp=array[byte]of byte;
Var a:arrtyp;
New(a);
A^[0]:=0;
Dispose(a);
</td></table>
C++ :
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
char* a=new char[256]; // czytać od prawej!!!
a[0]=0;
delete a;
</td></table>
albo:
```cpp
char* a; // wskaznik do typu
a=new char[256]; // przydzielamy pamięc na tablice danego typu
*a=0;
*(a+0)=0; // to samo
delete [] a;
</td></table>
new char[256] oznacza: zaalokuj 256-elementową tablicę charów.
new to odpowiednik pascalowego new - jest on operatorem (funkcją), alokuje on pamięc a następnie wywołuje konstruktory alokowanych obiektów (operator new []) albo alokowanego obiektu (new).
new służy do alokowania pojedyczych obiektów, new [] do alokowania tablic.
Analogicznie - delete i delete [].
Bezpośrednim odpowiednikiem new z pascala jest funkcja malloc z C.</p>
<a name="enum_type"></a><b>e)typy wyliczeniowe</b>
<p>Skoro już zacząłem. W poprzednim punkcie pokazałem jak zadeklarować typ wyliczeniowy tydzien dla pascala. Tylko w taki sposób można typ zadeklarować, nic wiecej się nie wyciśnie. Zmienne deklaruje się tak samo:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
type tydzien=(pn,wt,sr,cz,pt,sb,nd);
Var
a:pn..wt;
B:(abc,def,ghi,jkl);
C:0..255; {deklaracja typu byte}
</td></table>
Warto zapamiętać:
Byte,integer,char,boolean... wszystkie typy skokowe to typy wyliczeniowe. Pierwszy literał z definicji typu ma wartośc 0, więc jeżeli napiszemy:
```delphi
type tydzien=...
writeln(byte(pn)) {-> 0}
</td></table>
to otrzymamy 0</p>
<p>C++ pod tym względem ma większe mozliwości: Typ deklaruje się na dwa sposoby:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
enum tydzien {pn,wt,sr,cz,pt,sb,nd}
</td></table>
lub
```cpp
typedef enum {pn,wt,sr,cz,pt,sb,nd} tydzien
</td></table>
i też mamy zadeklarowany typ tydzien i tak samo pn ma w domyśle wartośc 0.</p>
<p>można też przypisać wartości dla poszczególnych literałów tworzących typ:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
enum liczby {malo=0, srednio=10, duzo=100, bduzo}; // bduzo=101
</td></table>
f)ciągi znaków
Tu jest potworna przepaść pomiędzy C i pascalem. No oczywiście wynika to z większego powiązania c z asemblerem. Ciąg znaków w pascalu (typ string) jest porównywalny do array[byte]of char, tyle że w bajcie o indeksie 0 jest zapisana jego długość. Np.
```delphi
Var A:string
Begin
A:='ala ma fioła';
Writeln(length(a),' ',byte(a[0]));
Writeln(a)
End;
</td></table>
Program 2 razy wypisze to samo, a potem cały napis. A[0] to char, a byte(a[0]) to zrzutowanie/konwersja typu. Writeln poza wyprowadzeniem tekstu wypisuje jeszcze 2 znaki: cr i lf : cariage return i line feed (#13 i #10)- powrót karetki (kursora) i przesunięcie linii. Writeln jest proceurą o zmiennej liczbie parametrów, przy czym przyjmuje wszystkie podstawowe typy jako argumenty:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Writeln(true) -> TRUE
Writeln(-5) -> -5
Writeln('ala ma fioła') -> ala ma wielkiego fioła
</td></table>
Szczerze mówiąc przyjmie też każdy typ wyliczeniowy, który wy sami zdefiniujecie...
```delphi
Type tydzien=(pn,...
Writeln(pn); {-> PN}
</td></table></p>
<p>W C/C++ jest inaczej: Ciągi zaznacza się pomiędzy podwójnymi cudzysłowami;
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
"ala ma fioła"
</td></table>
pozatym w C nie ma tam czegos takiego jak string. Jest tylko wskażnik do tablicy znaków - patrz punkt poprzedni tablice i wskażniki- jeszcze wcześniej.
```cpp
char* ala="ala ma fioła";
</td></table>
Ala to wskaźnik do chara ustawiony na "ala ma fioła"... Literał ala to po prostu wskażnik,</p>
<p>*ala to pierwszy znak z ciągu,
*(ala+i) i ala[i] to i-ty znak w ciągu np.:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
ala[0]='A';
*ala='A'; // załaduj 'A' pod adres ala;
*(ala+0)='A';
</td></table>
Tak z "ala ma fioła" robi się "Ala ma fioła"- trzy metody - do wyboru.</p>
Ciągi znaków muszą być zawsze zakończone znakiem 0, ale nie bójcie się. Przy takiej daklaracji jak powyższa, kompilator automatycznie to zero doda. Ewentualnie można zapisać coś takiego:
```cpp
char* ala="ala ma fioła\0" \\ ale wtedy będą 2 zera na końcu.
</td></table>
Funkcje obsługujące ciągi znaków znajdują się w zbiorze string.h warto się z nimi zapoznać, bo to podstawa do obsługi tekstów w dosowym c. Funkcja wypisujaca ciąg znaków to printf (nie jedyna), ale to specyficzna funkcja
i warto poczytać o niej.</p>
<p>Ogólnie działa prawie jak write. Jeżeli w pierwszym ciągu jest znak % to zaczyna formatować tekst:
printf(ala) -> ala ma fioła
printf("%s bardzo wielkiego !!!",ala) ->
-> ala ma fioła bardzo wielkiego !!!
Na miejsce znaków %s printf wstawia ciąg znaków z następnego argumentu.
printf("%s bardzo wielkiego !!!\ni ma %d lat",ala,10) ->
ala ma fioła bardzo wielkiego !!!
i ma 10 lat.
Znaczki %d oznacają , że kolejny parametr, ma być sformatowany, jak
liczba dziesiętna, a znaczek \n to po prostu znak nowej linii.</p>
<p>Aha, o czym wcześniej zapomniałem napisać. W pascalu jest możliwa taka operacja:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
var r,s,t:string;
r:=s+t;
</td></table>
Jest też funkcja , która to robi i nazywa się concat. Natomiast w C można to zrobić tylko albo ręcznie (oprogramować sobie samemu), albo za pomocą gotowej funkcji, np. strcat.</p>
C++ posiada typ std::string, zapewniający automatyczne zarządzanie pamięcią jak i przeładowane operatory, np.
```
#include <string>
int main() {
std::string a = "ala ma kota";
std::cout < a < " " < a + " i psa" < std::endl;
}
```
record/struct, niezależnie od tego jak się nazywa, chodzi o to samo. Deklaracje typów
Pascal:
```delphi
Type
Rectype=record
I:byte;
J:word;
K:record a,b:longint end
End;
</td></table>
C++
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
typedef struct {long a,b;} ktype;
typedef struct{
char i;
unsigned j;
ktype k;
} structtype;
</td></table>
lub (jednozanczne)
```cpp
struct structtype{
char i;
unsigned j;
struct {long a,b;} k;
}
</td></table>
Można oczywiście mieszać te dwa style... Tylko będzie to troszkę mniej czytelne. ;)</p>
<a name="declare"></a><b>h)deklaracje i odwołania do zmiennych:</b>
pascal:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
var rec=record
I:byte;
J:word;
K:record a,b:longint end
End;
</td></table>
C++ : Deklaracja podwójna: typu structtype i jednocześnie zmiennych rec i
rec1 oraz deklaracja rec2:
```cpp
struct structtype{
char i;
unsigned j;
struct {long a,b;} k;
} rec,rec1;
structtype rec2;
</td></table>
lub deklaracja samej zmiennej rec, bez deklaracji typu.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
struct {
char i;
unsigned j;
struct {long a,b;} k;
} rec;
</td></table>
odwołania:
c++:
```cpp
rec.i=0;
rec.j=20;
rec.k.a=2000000;
rec.k.b=400000;
</td></table>
pascal (czymś to się w zasadzie różni?):
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
rec.i:=0;
rec.j:=20;
rec.k.a:=2000000;
rec.k.b:=400000;
</td></table>
Ale pascal ma też wielkie ułatwienie:
```delphi
With rec do
Begin
I:=0
J:=20;
k.a:=2000000;
k.b:=400000
End;
</td></table>
Albo:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
With rec do
Begin
I:=0
J:=20;
With k do
Begin
a:=2000000;
b:=400000
End;
rec.i:=0 {w środku też tak można !}
End;
</td></table>
Odwołania do zmiennych tworzonych dynamicznie.
Załóżmy ...
Pascal:
```delphi
Var rec:^rectype; {wg. deklaracji powyższej}
New(rec);
Rec^.i:=0;
Rec^.j:=20;
Rec^.k.a:=2000000;
Rec^.k.b:=400000;
With rec^ do
Begin
I:=0
J:=20;
k.a:=2000000;
k.b:=400000
End;
Dispose(rec);
</td></table>
C++ :
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
structtype* rec; // wg. powyższej deklaracji typu
rec=new structtype;
rec->i=0;
rec->j=20;
rec->k.a=2000000;
rec->k.b=400000;
delete rec;
</td></table>
albo (jednoznaczne):
```cpp
rec=new structtype;
*(rec).i=0;
*(rec).j=20;
*(rec).k.a=2000000;
*(rec).k.b=400000;
delete rec;
</td></table>
A wracając do wskaźników... :) Jeden ze sposobów gmatwania...
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
rec=new structtype;
*(rec+0).i=0;
*(rec+0).j=20;
*(rec+0).k.a=2000000;
*(rec+0).k.b=400000;
delete rec;
</td></table>
3)porównania,warunki,pętle
3)porównania,warunki,pętle
No cóż przebrnąłem przez wskaźniki , tablice, wskaźniki do tablic, rekordy i wskaźniki do rekordów... Brrr. Nigdy nie będę pisał książek!
a)porównania:
Każdy to zna (pascal):
a (not(a=b))
a tak to samo wygląda w c++
a!=b -> (!(a==b))
Znaczek ! to ogólnie przyjęte w c zaprzeczenie (not)
</p>
Natomiast pascalowe
```delphi
a=b
</td></table>
to w C++:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
a==b
</td></table>
Co tu rozumieć? Tyle że w obu językach przypisanie jest inne od porównania.
b)warunki
I tu się zaczyna robić znowu ciekawie.
Przypuśćmy, że mamy coś porównać do 0, albo sprawdzić czy się zeru nie równa (=0/==0 albo <0/!=0)
Ci co znają c++ śmieją się, że jest to w tym języku takie proste
Wystarczy :
If(!c) - gdy chcemy sprawdzić czy c=0 (gdy nie c)
If(c) - gdy sprawdzamy, czy c<>0 (gdy c)
A teraz sztuczka pascalowa:
```delphi
Var c:byte;
Begin
If not boolean(c)then - czy c=0
If boolean(c)then - czy c<0;
End;
</td></table>
UWAGA: dopasowanie (rzutowanie/konwersja) typów musi być rozmiarowe. Tak więc w dosowym pascalu można tak robić tylko z jednobajtowymi zmiennymi. Natomist w delphi są typy: wordbool (2 bajty) i longbool (4 bajty) ;-)
Ale do rzeczy:
Warunek w pascalu ogólnie ma postać:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
If wartosc_bool
then
Begin
End
Else
Begin
End
</td></table>
Tych beginów i endów wcale nie musi być, właszcza kiedy potrzeba jednej instrukcji. Wrtosc_bool to ogólnie wynik wyrażenia logicznego np.:
Ala_kupi_mleko jeśli będzie sklep_otwarty i ma_pieniadze, a jeśli nie, to Ala_pojdzie_do_domu...
```delphi
If(sklep_otwarty and ma_pieniadze)
Then Ala_kupi_mleko
Else Ala_pojdzie_do_domu;
</td></table>
W c++
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
if(wartosc){
}else{
}
</td></table>
W c++ natomiast dla wartosci różnej od zera wykona się {} (tu nie ma then), a dla wartości 0 wykona się else{}. Tak samo tych znaczków też nie musi być.
```cpp
if(sklep_otwarty && ma_pieniadze)Ala_kupi_mleko
else Ala_pojdzie_do_domu;
</td></table>
Aha:
&& - to jest logiczny boolowski and
|| - to jest logiczny boolowski or
! - to jest zaprzeczenie boolowskie not
&& = and - sprawdza, czy obie wartości są<>0
= or - czy któraś z nich<>0
Operatory bitowe natomiast to zupełnie co innego:
& - and 3 and 1=1 ... 3&1==1
| - or 2 or 1=3 ... 2|1==3
<sup> - xor 3 xor 1=2 ... 3</sup>1==2
! - not trochę inaczej działa :
dla bajta działa jak xor $ff (255)
dla worda xor $ffff (65535)
dla longinta xor $ffffffff (bduzo-1)
pascalu wartość wartosc_bool musi być true żeby się spełniło then, jeśli nie spełni się else. To jest to proste.
A teraz powiążcie to i to co napisałem wcześniej. Boolean(wartosc) zwraca zawsze true, jeżeli wartosc<>0. Może być -100 nawet i tak będzie true.
To łatwo sprawdzić:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Var i:shortint; {-128..127 typ wyliczeniowy}
Begin
For i:=-128 to 127 do
If boolean(i)=false then writeln('false dla i=',i)
End;
</td></table>
Albo prościej:
```delphi
Var i:shortint; {-128..127 typ wyliczeniowy}
Begin
For i:=-128 to 127 do
If not boolean(i) then writeln('false dla i=',i)
End;
</td></table>
Taki sam wynik będzie w c++
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
int i;
for(i=-128;i<=127;i++)
if(!i)printf("warotść 0 dla i==%d\n",i);
</td></table>
i++ to mniej więcej pascalowe i:=i+1, ale szybciej się pisze. Podobnie i-- , ++i , --i, ale na to specjalnie poswięcę cały rozdział.
Oczywiście, że bez sensu ten warunek. Przecież pisałem, że c nie ma typów boolowskich, co strasznie ułatwia życie i upraszcza kod aż do absurdu.
Dobrze napisałem? Dobrze... W ostateczności...
Delphi:
```delphi
Var p:pointer;
P=nil; {nil=pointer(longint(0))}
Longbool(p) -> false
</td></table>
W c++
Ten sam tok rozumowania się nie uda bo null to 0 lub 0L. 0L, to long(0), czyli 2 lub 4 bajtowe zero (obejrzyjcie sobie plik np. stdlib.h), ale dla pokazania, ża mam rację:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
void* p;
p=null; //rownie dobrze możecie pisac p=0
p -> 0 - oczywiste, sam przed chwilą to napisłem.
</td></table></p>
c)pętle
c++ :
```cpp
while(wartość){
}
</td></table>
to pascalowe
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
while(warunek)do
begin
end
</td></table>
i to dokładnie, z uwzględnieniem tego, co napisałem o warunkach: boolean(wartość)=warunek.
do{
}while(warunek)
różni się od
repeat
until (warunek)
tym, że z repeat until wychodzi się, gdy spełniony jest warunek. Z do-while, gdy nie spełniony.
Tak więc gdy zapisac to tak:
Repeat
Until warunek
I
do{
}while (!(warunek))
to obie te pętle robią się identyczne.
Pętla for...
Opiszę najpierw for pascalowe, to będzie chwilka:
```delphi
For indeks:=dol to gora do
Begin
End;
</td></table>
Lub
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
For indeks:=gora downto dol do
Begin
End;
</td></table>
Gdzie dol, to dolna warosc indeksu petli, a gora to gorna wartosc. Pierwsza pętla zaczyna od odłu i idzie co 1 do gory, druga na odwrót od gory idzie w doł, aż go usiągnie, też co jeden. I nie ważne, czy indeks bedzie typu integer, boolean, czy char, czy też będzie to inny typ wyliczeniowy.
```delphi
Var i:boolean;
For i:=false to true do...
Var i:byte;
For i:=0 to 255 do...
Var i:char;
For i:=#0 to #255 do...
for i:=char(0) to char(255) do... {char(0)=#0=^@<>chr(0) - bo chr to funkcja, a nie stała}
for i:=^@ to char(255) do...
Type tydzien=(pn,wt,sr,cz,pt,so,nd);
Var i:tydzien;
For i=pn to nd do...
</td></table>
Natomiast for w c++...
Gość, który wpadł na ten pomysł powinien dostać nobla. Serio, zasłużył. C i C++ to jedyne języki w których pętla for nie jest pętlą skokową o pojedynczym indeksie. Można czasem jedną pętlą for załatwić to, co w pascalu przy pomocy dwóch, a bywa, że i więcej.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
for(ustawienia strtowe;warunek kontnuowania;do wykonania po jednym przejsciu){}
</td></table>
ustawienia startowe:
nie musi być jedna zmienna: To jest to miejsce, gdzie się przypisuje wartości początkowe.
np.
```cpp
int i;
long j;
int* p;
for(i=1,j=2,p=null;;)
</td></table>
warunek kontynuowania:
całe wyrażenie, jakie chcecie, byleby zrozumiałe dla kompilatora. Pętla będzie kontynuowana, gdy warunek będzie miał wartość różną od zera.
Np.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
int* p;
for(p=null;*p==22;)
</td></table>
do wykonania po jednym przejściu :
To jest to, co się wykonuje po wykonaniu jednego przejścia pętli.
```cpp
int* a=(1,2,3,4,5,6,22); // wskaźnik do tablicy intów
int* p;
for(p=a;*p!=22;p++);
</td></table>
Co? O czymś zapimniałem? Że co, że p++ ? No tak, zapomniałem. Wskażniki w c/c++ można zwiększać i zmniejszać jak inne zmienne. Choć nie do końca tak samo. Dla przypomnienia wskaźnik do typu jest jednocześnie wskaźnikiem do tablicy danego typu. Można wskaźnik traktować jak w pascalu jako stałą i wg. niego się orientować ale można go zwiekszać i zmniejszać. Wskaźnik zwiększa swój adres co co rozmiar komórki. Tylko w szczególnym przypadku gdy rozmiar komórka będzie typu np. char, będzie się zwiększał/zmniejszał co bajt. Dla powyższego przykładu zwiększa się co sizeof(int) (==2). Pętla zakończy się oczywiście, gdy p będzie wskazywało na a[6]==22;
W pętli for nie trzeba wypełniać wszystkich tych pól, zmiany i sprawdzanie indeksów można wykonywać wewnątrz w pętli. Zawsze przecież istnieje break i znaczy to samo co w pascalu. W szczegołnym przypadku pętla:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
for(;;){
}
</td></table>
jest pętlą nieskończoną.</p>
4. Operatory matematyczne/operacje zmiennoprzecinkowe
4. Operatory matematyczne/operacje zmiennoprzecinkowe
a)przypisania</p>
mówi wam coś takiego? += -= *= /= otóż:
a+=b; -> a=a+b;
a-=b; -> a=a-b;
a*=b; -> a=a*b;
a/=b; -> a=a/b; //div lub dzielenie zmiennoprzecinkowe - oba w zależności od typów (powyższe dla wszystkich typów)
a%=b; -> a=a%b; //mod
a|=b; -> a=a|b; //or bitowe
a&=b; -> a=a&b; //and bitowe
a=b; -> a=ab; //xor bitowe..łeee ;-)
a<<=b;-> a=a<<b; // shl ... jak wyżej.
a>>=b;-> a=a>>b; // shr ...
{a te tylko dla stałoprzecinkowych)
</p>
Tylko się nie rozpędźcie a!=b wcale nie znaczy a=a!b - takiego wyrażenia nie ma :-P, pozatym, jeśli coś pominąłem, to już macie niejakie pojęcie o co chodzi.
Operator / dla stało przecinkowych zachowuje się jak div- dzieli bez reszty, natomiast gdy dzielnikiem jest jakis float, daje wynik zmiennoprzecinkowy.
b)operatory incrementacji/decrementacji
++ --
i++ ++i i-- --i?
Są to operatory incrementacji i dekrementacji o jeden. Bardzo specyficzne operatory bo można je wykorzystać w wewnątrz wywołań... Przykład, bo inaczej nie zrozumiecie.
```cpp
void strcpy(char* src,char* dst){
int i;
i=length(src);
while(i)dst[i--]=src[i]
}
</td></table>
ta funkcja kopiuje ciąg znaków z src do dst łącznie z kończącym zerem.
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
void strcpy(char* src,char* dst){
int i;
i=length(src);
while(i--)dst[i]=src[i]
}
</td></table>
ta kopiuje, ale bez konczącego zera.
<table width="100%" bgcolor="black" cellpadding="8">
void strcpy(char* src,char* dst){
int i;
i=length(src);
while(--i)dst[i]=src[i]
}
</td></table>
ta kopiuje bez konczącego zera i pierwszego znaku w ciągu.
```cpp
void strcpy(char* src,char* dst){
int i;
i=length(src);
while(i)dst[i]=src[i--]
}
</td></table>
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
void strcpy(char* src,char* dst){
int i;
i=length(src);
while(i)dst[--i]=src[i]
}
</td></table>
Te dwie natomiast tworzą z "Ala ma fioła" "la ma fioła"
No dobrze... Wiecie, już co znaczy 'wewnątrz wywołań'. A teraz kolejnośc:
--i -> i zostanie zmniejszone ZANIM zostanie odczytana z niego wartość.
i-- -> i zostanie zmniejszone ZARAZ PO odczytaniu z niego wartości.
Wszystko jasne?
Aha te operatory dotyczą TYLKO typów stałoprzecinkowych.
c)operacje zmiennoprzecinkowe</p>
Nie będę opisywał tych operacji, bo sa takie same jak w pascalu, funcje, etc. Ale chcę zwrócić na jedno uwagę: na konwersję typów zmiennoprzecinkowych (float, single,double,... whatever)
Otóż w pascalu aby zapisać jakiegoś floata do inta trzeba było uzyć funkcji trunc, ewentualnie round, bo inaczej wyskakiwał błąd kompilacji.C++ AUTOMATYCZNIE wybiera funkcje zaokrąglenia (trunc), więc trzeba Bardzo uważać, aby nie zrobić sobie kuku...
```cpp
int i;
i=sin(3.14/3); //pójdzie, ale ... mocno nie ta wartość co trzeba.
</td></table>
<a name="decl_ag"></a><h3>5. Deklaracje zmiennych - once again.</h3>
<p>Otóż do tej pory starałem się deklarować zmienne na sposób przyjęty w pascalu, zanim ich uzyje. Jedynie przy new pokazałem dwa sposoby. Teraz napiszę, że w c++ deklarować zmienne można naprawdę w wielu dziwnych miejscach...
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
for(int i=0;....;..);
int i;
</td></table>
w tym przypadku i będzie widziane tylko wewnątrz pętli, zas powtórna deklaracja spowoduje bład albo w najlepszym wypadku ostrzeżenie kompilatora.
```cpp
main(){
if (prawda){
char* q="qrde, gdzie ja jestem?";
}
}
</td></table>
Widziana tylko w bloku if'a
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
char i;
fun(){
short j;
}
</td></table>
jedna globalna, druga lokalna.</p>
Itepe, itede, etc. Bardzo szybko załapiecie, gdzie wolno, a gdzie nie wolno deklarować i dlaczego.
6. Przeciążenia (override)
6. Przeciążenia (override)
Tego zdecydowanie nie było w pascalu...
```cpp
int fun(char* a){
... body
}
void fun(integer b,long d){
.... body
}
</td></table>
Dwie funkcje o takiej samej nazwie... I wiecie, że to pójdzie. To dlatego, że do kompilacji c++ buduje nazwy funkcji z nazwy, którą wy nadaliście + dodatkowo typów parametrów wywołania. Więc aby używać takich samych nazw dla wielu funkcji musicie zadbać, aby kompilator mógł je rozróźnić.</p>
<p>Ten mechanizm istnieje już w delphi. Jak wiele innych elementów, które wcześniej należały tylko do C++. (4programmers.net- thnx)</p>
<p>Np. komentarze: //</p>
<a name="param"></a><h3>7. parametry wywołania programu.</h3>
<p>Pascal: Paramstr(i) zwraca i-ty ciąg znaków wywołania programu, a paramcount to ilość tych parametrów. W szczególności paramstr(0) jest ścieżką i nazwą wykonywanego programu.</p>
<p>C++
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
main(int argc,char** argv){
}
</td></table>
lub
```cpp
main int argc;char* argv[]{
}
</td></table>
Osobiście nie lubię drugiej formy deklaracji, mimo że jest jak najbardziej poprawna...</p>
<p>Bez przerażenia (czytać od prawej) argv jest to wskaźnik do wskaźnika do znaku. A że wskaźnik do czegoś jest wskaźnikiem do tablicy czegoś, to jest to wskaźnik do tablicy wskaźników do tablicy znaków (ciągu znaków). Zaś argv[0] to nazwa i ścieżka programu z zerem na końcu oczywiście. Argc to ilość parametrów wywołania. Odpowiedniki paramstr i paramcount.
<a name="lib"></a><h3>8.Biblioteki/moduły</h3>
<p>Jak się deklaruje użycie modułu w pascalu? Ano tak:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Uses nazwa_modulu1,nazwa_modulu2;
</td></table>
A tak w c++ :
```cpp
#include <nazwa_biblioteki1.h>
#include <nazwa_biblioteki2.h>
#include "nazwa_biblioteki3.h"
</td></table>
Znaki <> oznaczają, że kompilator ma szukać plików h (h jak header - nagłówek stąd: plik nagłówkowy) tylko w podstawowej defaultowej scieżce. Znaki: "" oznaczają, że kompilator poza defaultową biblioteką, w której trzymane są pliki h ma jeszcze przeszukać waszą, w której znajduje się źródło programu. Tak się
oznacza zwykle własne pliki.</p>
<p>Tak się mniej więcej przedstawiają odpowiedniki bibliotek: Mniej więcej, bo funkcje i procedury różnie są porozrzucane.
<table bgcolor="black"><tr><td bgcolor="FFFDE6">
```delphi
pascal
</td>C++
```
</td></tr>system
```
</td>stdlib, stdio
```
</td></tr>crt
```
</td>conio
```
</td></tr>graph
```
</td>graphics
```
</td></tr>dos
```
</td>dir
```
</td></tr></table></p>
9.Ciekawostki
9.Ciekawostki
Czy zauważyliście, że nie przed żadnym endem, untilem, znakiem } jest średnik? To taka specyfika obu języków.
Czy wiecie, że jest coś pośredniego jeszcze pomiędzy c/c++ i pascalem i że jest to następna produkcja N. Wirtha i nazywa się modula2. To musi być dopiero pokręcony język. Jeśli ktos zaposiada kompilator, ta ja chętnie, chocby po to, żeby się nad nim poznęcać. E... przetestować jego możliwości.
Model pamięci tiny pozwala produkować pliki typu com, ale o budowie powiedzmy command.com, a nie tych sciąganych z inetu. (akurat dzisiejszy command jest faktycznie plikiem typu exe, ale w starszych wersjach Bill G. był mniej
zakłamany (- due to 4programmers)
W pascalu też da się tworzyć stałe typu pointer:
C++:
```cpp
const void* ekran=0xa0000000L;
</td></table>
pascal:
<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
const ekran:pointer=pointer(longint($a0000000));
const nil=pointer(longint(0));
</td></table>
W c++ nie ma czegoś takiego jak set, ale... W typie set każda wrtość ma przyporządkowany jeden bit- po kolei i rozmiar zmiennej jest zaokrąglony zawsze w górę...Tak więc set of byte ma rozmiar 256 bitów=32 bajty... Wiecie do czego zmierzam? Ano:</p>
Uwaga to jest przykład dla kompilatora dosowego!!!!
```delphi
Type bytearr=array[0..31]of byte;
Var a:set of byte;
Begin
a:=[];
Writeln( (4 in a) ); {wypisze: false}
Writeln( (8 in a) ); {wypisze: false}
bytearr((@a)^)[0]:=1 shl 4; {4 bit to bajt 0 bit 4}
bytearr((@a)^)[1]:=1 shl 0; {8 bit to bajt 1 bit 0}
Writeln( (4 in a) ); {wypisze: true}
Writeln( (8 in a) ) {wypisze: true}
End.
</td></table>
n-ty bit to: bajt n div 8 bit n mod 8</p>
<p>Powiem wam jeszcze, że w c++ można programować operatory (np. +,- i tym podobne). Czy już wiecie jak zasymulowć taki typ w c++? No to pomyślcie, bo to jest banalne.</p>
<a name="other"></a><h3>10. O czym nie wspomniałem?</h3>
<p>Np. o obiektach. O parametrach domyślnych funkcji w C++, o zmiennej ilości parametrów wywołania funkcji, czego nie ma w pascalu. Chociaż można napisać tak program, że nie będzie to niezbędne. ...O makrach. O const w pascalu i c++ ; na ten temat możnaby wiele...</p>
Część druga - uzupełnienia:
1. Adresy i wskaźniki cd.
Nie spomniałem ja wyciągnąć adres danej struktury danych, czy funkcji.
otóż w pascalu wygląda to tak:
```delphi
procedure proc;
begin
writeln('whatever') {write + cr+lf}
end;
var p:pointer;
a:int {jakikolwiek typ}
begin
p:=@a; {p wskazuke teraz na a}
p:=@proc
end;
A w c++ :
void proc(){
printf("whatever\n"); // \n -> cr+lf
}
void main(){
void* p;
int a; // any type You want
p=&a;
p=proc;
}
O co chodzi?
Ano o to, że w pascalu zawsze używa się znaczka @, a w C++ nie zawsze uzywa się &, dlatego że literał oznczający nazwę procedury jest traktowany jak wskaźnik. Więc nawet jeśli kompilator przyjąłby zapis p=&proc, to w wyniku nie otrzymalibyśmy adresu
procedury proc, tylko adres wskaźnika do niej. A zatem p=proc jest traktowane jak skopiowanie wartości wskaźnika proc do p.
Tworzenie wskaźników:
podstawową metodą jest użycie funkcji ptr(seg,ofs:word) (pascal) lub macra MK_FP(unsigned seg,unsigned ofs) (c++ MK_FP - make far pointer)
funkcja ptr zwraca typ pointer, który może być przypisany do każdego typu wskaźnika (np. integer, char itepe) MK_FP rownież zwraca wksaźnik nietypowany: void* , ale jeżeli chcemy przypisać wartośc wskaźnikowi typowanemu, to w c++ trzeba dokonać konwersji typów, np.:
int* p=(int*)MK_FP(seg,ofs);
Konwersje typów (rzutowanie typów)</li>
</ol>
Więc pokolei... Już częściowo to było, kilkakrotnie.
Otóż istnieją dwa rodzaje konwersji: konwersja co do wartości i konwersja rozmiarowa. Konwersja co do wartości zacodzi pomiędzy typami tego samego rodzaju i w większości wypadków jest wykonywana automatycznie, tak w pascalu, jak i w C/C++:
var i:integer;
j:longint;
begin
j:=10;
i:=j
end.
void main(){
int i;
long j;
i=10;
j=i;
}
Zrozumiałe? Chyba jasne tu konwersja jest wykonywana automatycznie.
Ale tu juz nie:
var i:integer;
c:char;
begin
c:=#65 { 'A' }
i:=integer(c); {i:=65}
end.
Tu trzeba wyjaśnić pewna nieścisłość. Typ char, jest typem sztucznym. Bardzo sztucznym. C/C++ ma dużo zdrowsze podejście. Dlaczego? Dlatego że skoro w pascalu nie mozna dodawać znaków do siebie, to najpierw powinna być konwersja rozmiarowa z char to byte ewentualnie shortint, a dopiero później do integera...
i:=integer(byte(c)); {to oczywiście przejdzie w kompilacji}
i:=integer(shortint(c)); {to też}
... tymczasem kompilator wcale nie krzyczy, To w końcu co? Jest to odzielny typ nie mający nic wspólnego z liczbami, czy typ stałoprzecinkowy, którego nie mozna poddać operacjom arytmetycznym? Również gdy napiszemy coś takiego:
i:=word(c);
i:=longint(c);
Kompilator umarł. Nie ma go. Zero błędu. W takim razie dlaczego nie działa coś takiego...
var r:real; {single/double/... etc.}
i:longint; {char/byte/integer/longint/...}
begin
i:=r
end.
...przeciez to logiczne następstwo? (Mutacje zmienno-i stałoprzecinkowych dowolne.)
Otóż jest to jedyna domyślna konwersja rozmiarowa w pascalu: char do byte, ale nie z powrotem:
var c:char;
begin
c:=byte(1);
end.
To w żaden sposób nie przejdzie. To jest niekonsekwencja N. Wirtha. Z tej niekonsekwencji wynika następna:
var a:array[byte]of char;
s:string; {string=string[255]}
begin
s:='Ala ma fioła';
a:='Ala ma fioła';
end.
To też nie przejdzie, Choć powinno. Skoro string może, to czemu nie tablica. Albo skoro żadna tablica, to dlaczego akurat string? No cóż, skoro char, to tym bardziej string jest sztuczny.
Owa niekonsekwencja nie umniejsza pascala. Nie powoduje, że jest on gorszy, jedynie powoduje, że w tym miejscu jest mniej logiczny od c/c++ !!! To jest coś, czego programista musi się nauczyc na pamięć, nie na logikę. Więc nie dziwcie się, że znający c++ mają czasem tendencje do zadzierania nosa. Ten język jest bardziej logiczny. Ale jedynym całkowicie logicznym językiem jest język maszynowy. Każdy język wyzszego poziomu od jezyka maszynowego (*
100 na 10 punktów !
Hmm, chyba cos miejscami style się zepsuły.. :/
Bardzo dobry artykul, czy bedzie kiedys dokonczony?
tia, formatowanie tekstu działające tylko pod IE. pod innymi przeglądarkami tekst jest nieczytelny.
Artykuł zupelnie nieczytelny. Jakies problemy z kodowaniem ("±" i inne podobne zamaist znakow), te ramki czarne wokół pól z kodem to porażka. Dziwne bo wczoraj czytalem wlasnie ten art. i bylo ok.
bardzo... trudno sie go czyta :/ :/ :/
Niezłe
Swietny pomysł !! Uwazam ze jest to bardzo przydatny artykuł. Jednak dziwi mnie że w spisie typów zmiennych nie pojawiło się extended. Czy jest to w c++ long double ?
Bardzo wartościowy artykuł !!!
Dobra robota manfredek [browar]