C i pascal, czyli tam i z powrotem

kncik tfórzuff_4p

Jeszcze w budowie !

Autorem jest Flabra, ja (kapustka) jestem tylko jego sekretarką i ponieważ uważam, że oryginalny art. jest równie ciekawy co trudny w czytaniu - podjąłem się jego przepisania i przeformatowania.

C++ i pascal, czyli tam i z powrotem

Spis tresci

  1. Wstęp
  2. Semantyka
  3. Typy zmiennych

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 kopilacja 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 paskalu 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 paskalu 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 ;-)
</p>

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 daklarowanie funckji 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)podstawowe

Róż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>brak ``` </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. Niestety brak w pascalu odpowiednika, natomiast byłby to cardinal/longword z delphi. I na dodatek nie da się tego zadeklazowć. Deklaracja... ```delphi type ulong=0..maxlongint*2+1;

maxlongint*2+1=(dużo-1)*2+1=(2^31-1)*2+1=2^32-2+1=2^32-1=bdużo-1.

</td></table>
...wywala błąd kompilacji: ... overflow ...</p> 
<p>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.</p>
<a name="bool_type"></a><b>b)zmienne typu boolowskiego.</b>
<p>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.</p>
<p>To jest wstęp, reszta jest przy warunkach, bo boole jednoznacznie kojarzą się z warunkami.</p>
<a name="pointers"></a><b>c)wskazniki</b>
<p>To już częściowo było:

void* = pointer
int*= ^integer;

Odwołania:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```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> ```cpp main{ int* i=new int; *i=1; delete i; // albo: i=(int*)malloc(sizeof(int*)); *i=1; free(i) }
</td></table>
lub (jednoznaczne)

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

Rozmiar wskaźników w C++ to 2/4 bajty. 2-bajtowe to wskażniki bliskie:

Np.

```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.

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

W modelach medium/small/tiny, segment danych ma tylko 64 kb, jest to obojętne, najwyżej zuzżjecie więcej pamięci.

d)tablice

Pascal:

```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>
<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):

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
var a:array[boolean]of byte;
tablica ma 2 komórki.

</td></table> odwołania do tablicy: ```delphi a[false]:=10; a[true]:=20;
</td></table>
Tablica dwuwymiarowa:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
var a:array[boolean,char]of integer;

</td></table> odwołanie: ```delphi a[true,#13]:=-1;
</td></table>
ale można się też do tablicy odwoływać na sposób C++:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
a[true][#13]:=-1;

</td></table> można zadeklarować np. typ wyliczeniowy: ```delphi type tydzien=(pn,wt,sr,cz,pt,sb,nd);
</td></table>
i tablicę :

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```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: ```delphi b[sr][#20][false]:=-10;
</td></table>
I tak dalej, i tak dalej na wszelkie możliwe sposoby.</p>
<p>C++ : Tablice są bardziej ograniczone:
Deklaracja wygląda następująco np.:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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... ```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>
<p>Tablica trójwymiarowa:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```cpp
char b[5][10][20];

</td></table> przypisanie : ```cpp b[1][2][3]=0; *(*(*(b+1)+2)+3)=0; // to samo
</td></table></p>
<p>Tablice tworzone dynamicznie:
W c++ wskaźnik do danego typu, jest również wskaźnikiem do tablicy danego typu.

Pascal:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Type arrtyp=array[byte]of byte;
Var a:arrtyp;

New(a);
A^[0]:=0;
Dispose(a);

</td></table> C++ : ```cpp char* a=new char[256]; // czytać od prawej!!! a[0]=0; delete a;
</td></table>
albo:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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: 256-elementowa tablica charów, new to odpowiednik pascalowego new, tyle tylko, że to macro, a nie procedura.</p> e)typy wyliczeniowe

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:

```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:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
type tydzien=...
writeln(byte(pn)) {-> 0}

</td></table> to otrzymamy 0</p>

C++ pod tym względem ma większe mozliwości: Typ deklaruje się na dwa sposoby:

```cpp enum tydzien {pn,wt,sr,cz,pt,sb,nd}
</td></table>
lub

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

można też przypisać wartości dla poszczególnych literałów tworzących typ:

```cpp enum liczby {malo=0, srednio=10, duzo=100, bduzo}; // bduzo=101
</td></table>
<a name="string_type"></a><b>f)ciągi znaków</b>
<p>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. 

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```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: ```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...

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Type tydzien=(pn,...

Writeln(pn); {-> PN}

</td></table></p>

W C++ jest inaczej: Ciągi zaznacza się pomiędzy podwójnymi cudzysłowami;

```cpp "ala ma fioła"
</td></table>
pozatym 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.

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

*ala to pierwszy znak z ciągu, *(ala+i) i ala[i] to i-ty znak w ciągu np.:

```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>
<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:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

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>

Aha, o czym wcześniej zapomniałem napisać. W pascalu jest możliwa taka operacja:

```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>
<a name="record_type"></a><b>g)rekordy/struktury</b>
<p>record/struct, niezależnie od tego jak się nazywa, chodzi o to samo. Deklaracje typów
Pascal:

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="FFFDE6">
```delphi
Type
  Rectype=record
            I:byte;
            J:word;
            K:record a,b:longint end
          End;

</td></table> C++ ```cpp typedef struct {long a,b;} ktype; typedef struct{ char i; unsigned j; ktype k; } structtype;
</td></table>
lub (jednozanczne)

<table width="100%" bgcolor="black" cellpadding="8"><td bgcolor="F7FDFF">
```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>

1 komentarz

hej, kto ukradł tagi?