C i pascal, czyli tam i z powrotem

C++ i pascal, czyli tam i z powrotem

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 C++ uważa 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 zanczy, że podstawowe zasady nie są prawdziwe.

Chce również uprzedzić, że pisząc to nie chce robić konkurencji Adamowi Boduchowi, 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ę.

1.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:

const A='abrakadabra';
begin
  writeln(a)
end.

Zostanie skompilowane i wykonane bez żadnej reakcji ze strony kompilatora. Natomiast:

const char* A='abrakadabra'
main(){
  printf(a)
}

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++, tylę, że wszelkie typy złozone rozkładane sa na elementy pierwsze. Potem sprawdza się syntaktyczną poprawność źródła programu (dzieki Dryo), 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.
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 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;

I w ten sposób uzyskujemy dwa nowe typy danych.
Natomiast w C++ każdą deklarację typu poprzedza się słówkiem typedef:

typedef char nowytyp;
typedef int drugityp;

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.

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 (choc 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.

C++ :

int fun(){
  return 10
}

lub zapis jednoznaczny:

fun(){
  return 10
}

pascal:

function fun:integer;
begin
  fun:=10
end;

lub zapis jednoznaczny:

function fun:integer;
begin
  result:=10
end;

Tu trzeba wyjaśnić cztery rzeczy:

Po pierwsze:
Typ int jest typem domyślnym dla funkcji, więc nic nie trzeba wpisywać, a komilator 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:

begin
  result:=10;
  exit
end;

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! Dożo, dużo więcej, czytaliście o kompilacji) niż pascal.

Po trzecie:
znak := to w pascalu znak przypisania, tak samo jak = w c/c++

Po czwartee:
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).

Chcemy zadeklarować procedurę proc, która wypisuje rożne rzeczy:

procedure proc;
begin
  write('różne rzeczy')
end;

void proc(){
  printf("różne rzeczy")
}

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ć. ;-)

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.

e)parametry wywołań funkcji/procedur
Zacznę od przykładu (od nagłówków jednakowych procedur):

type pinteger:^integer;
procedure proc(a,b:char;c:integer;var d:char;e:pinteger)

void proc(char a,char b,int c,char& d,int* e)

Rozumiecie?
Po pierwsze:
^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.

^integer to to samo, co int* .

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

procedure proc(a:char;b:char;c:integer;var d:char;e:pinteger)

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

Procedure proc(a:char)
void proc(char a)

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

Po szóste(czego nie widać):

void proc(char a,char b,int c,char & d,int *e)
void proc(char a,char b,int c,char &d,int * e)

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.

A więc, czym jest zmienna a w poniższej deklaracji?

Jakis_typ jakas_funkcja(int * &a)

Oczywiście jest to referencja do wskaźnika do inta, czyli po pascalowemu:

Type typ_posredni:^integer;
Function jakas_funkcja(var a:typ_posredni):jakis_typ;

(np. tak zwraca pointer procedura getmem(var p:pointer,...))

A w mniej chorym zapisie:

Jakis_typ jakas_funkcja(int*& a)

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 jest to tak samo jak w pascalu 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.

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

Begin
  Writeln('abrakadabra')
End.

   Niclaus Wirth is creator of pascal

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.

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
}

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

g)forłardowanie (forwarding)
Już przykład był, teraz szczegóły...

Pascal:

function abc(i,j:integer):integer; forward;

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;

ALBO jednoznaczne

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;

C++ :

int abc(int i,int j); 

void def(){
  int a,b,c;
  a=5;
  b=6;
  c=abc(a,b)
}

int abc(int i,int j){
  return (i+j) 
}

LUB jednoznaczne

abc(int,int); //lub abc(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) 
}

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.

C++             pascal          zakres
unsigned char   byte,char        0  do   255 {*}
signed char     shortint      -128  do   127 {*}
unsigned short  word             0  do 65535
unsigned int    word             0  do 65535
unsigned        word             0  do 65535 (**)
signed int      integer     -32768  do 32767
int             integer     -32768  do 32767
signed short	integer	    -32768  do 32767
signed long     longint      -dużo  do duzo-1 (***)
long            longint      -dużo  do dużo-1
unsigned long   brak             0  do bdużo-1 (****)

() 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 2duzo, czyli 2^32. Niestety brak w pascalu odpowiednika, natomiast byłby to cardinal/longword z delphi

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:

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;
main{
  int* i=new int;
  *i=1;
  delete i
  i=(int*)malloc(sizeof(int*));
  *i=1;
  free(i)
}

lub (jednoznaczne)

main{
  int* i;
  i=new int;
  *i=1;
  delete i
  i=(int*)malloc(sizeof(int*));
  *i=1;
  free(i)
}

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.

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

Np.

void near* a;

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 uzyje się modyfikatora far.

void far* a;

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.

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

d)tablice
Pascal:

var a:array[1..20]of integer;
type atype=array[byte]of word;

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.

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

var a:array[boolean]of byte;

tablica ma 2 komórki.

odwołania do tablicy:

a[false]:=10;
a[true]:=20;

Tablica dwuwymiarowa:

var a:array[boolean,char]of integer;

odwołanie:

a[true,#13]:=-1;

ale można się też do tablicy odwoływać na sposób C++:

a[true][#13]:=-1;

można zadeklarować np. typ wyliczeniowy:

type tydzien=(pn,wt,sr,cz,pt,sb,nd);

i tablicę :

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;
 {albo:}
b[sr][#20][false]:=-10;

I tak dalej, i tak dalej na wszelkie możliwe sposoby.

C++ :

Tablice są bardziej ograniczone:

Deklaracja wygląda następująco np.:

int a[10];

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

a[1]

to to samo, co <cpp<*(a+1)</cpp> - fajnie, nie?
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.

Tablica trójwymiarowa:

char b[5][10][20];

//przypisanie :
b[1][2][3]=0

Tablice tworzone dynamicznie:

W c++ wskaźnik do danego typu, jest również wskaźnikiem do tablicy danego typu.

Pascal:

Type arrtyp=array[byte]of byte;
Var a:arrtyp;

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

C++ :

char* a=new char[256]; // czytać od prawej!!!
a[0]=0;
delete a;

albo:

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;

new char[256] oznacza: 256-elementowa tablica charów, new to odpowiednik pascalowego new, tyle tylko, że to macro, a nie procedura.

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:

type tydzien=(pn,wt,sr,cz,pt,sb,nd);
Var 
  a:pn..wt;
  B:(abc,def,ghi,jkl);
  C:0..255;  {deklaracja typu byte}

Warto zapamiętać:
Byte,integer,char,boolean... wszystkie typy skokowe to typy wyliczeniowe. Pierwszy literał z definicvji typu ma wartośc 0, więc jeżeli napiszemy :

type tydzien=...
writeln(byte(pn)) {-> 0}

to otrzymamy 0
C++ pod tym względem ma większe mozliwości:

Typ deklaruje się na dwa sposoby:

enum tydzien {pn,wt,sr,cz,pt,sb,nd}
lub
typedef enum {pn,wt,sr,cz,pt,sb,nd} tydzien

i tez mamy zadeklarowany typ tydzien i tak samo pn ma w domyśle wartośc 0.

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

enum liczby {malo=0, srednio=10, duzo=100, bduzo} // bduzo=101

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.

Var A:string
Begin
  A:='ala ma fioła';
  Writeln(length(a),'    ',byte(a[0]));
  Writeln(a)
End;

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:

Writeln(true) -> TRUE
Writeln(-5) -> -5
Writeln('ala ma fioła') -> ala ma wielkiego fioła

Szczerze mówiąc przyjmie też każdy typ wyliczeniowy, który wy sami zdefiniujecie.

W C++ jest inaczej:

Ciągi zaznacza się pomiędzy podwójnymi cudzysłowami;

"ala ma fioła"

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.

char* ala="ala ma fioła"; 

ala to wskaźnik do chara ustawiony na ”ala ma fioła”

literał ala to po prostu wskażnik.
*ala to pierwszy znak z ciągu
*(ala+i), ala[i] to i-ty znak w ciągu np.

ala[0]='A';
*ala='A';   //<- załaduj 'A' pod adres ala;
*(ala+0)='A';

Tak z "ala ma fioła" robi się "Ala ma fioła"- trzy metody - do wyboru.

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:

char* ala="ala ma fioła\0", ale wtedy będą 2 zera na końcu.

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.

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.

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

var r,s,t:string;
r:=s+t; 

Jest też funkcja , która to robi i nazywa się :
concat

Natomiast w C++ można to zrobić tylko za pomoca funkcji.
np. strcat.

g)rekordy/struktury
record/struct, niezależnie od tego jak się nazywa, chodzi o to samo.

Deklaracje typów
Pascal:

Type
  Rectype=record
            I:byte;
            J:word;
            K:record a,b:longint end
          End;

C++

typedef struct {long a,b;} ktype;
typedef struct{
          char i;
          unsigned j;
          ktype k;
        } structtype;

lub (jednozanczne)

struct structtype{
         char i;
         unsigned j;
         struct {long a,b;} k;
       }

Można oczywiście mieszać te dwa style...
Tylko będzie to troszkę mniej czytelne. ;)

deklaracje zmiennych:
pascal:

var rec=record
          I:byte;
          J:word;
          K:record a,b:longint end
        End;

C++ :

Deklaracja podwójna: typu structtype i jednocześnie zmiennych rec i rec1 oraz deklaracja rec2:

struct structtype{
         char i;
         unsigned j;
         struct {long a,b;} k;
       } rec,rec1;
structtype rec2;

lub deklaracja samej zmiennej rec, bez deklaracji typu.

struct {
         char i;
         unsigned j;
         struct {long a,b;} k;
       } rec;

odwołania:

c++:

rec.i=0;
rec.j=20;
rec.k.a=2000000;
rec.k.b=400000;

pascal (czymś to się w zasadzie różni?):

rec.i:=0;
rec.j:=20;
rec.k.a:=2000000;
rec.k.b:=400000;

Ale pascal ma też wielkie ułatwienie:

With rec do
  Begin
    I:=0
    J:=20;
    k.a:=2000000;
    k.b:=400000
  End;

Albo:

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;

Odwołania do zmiennych tworzonych dynamicznie.

Załóżmy :

Pascal:

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);

C++ :

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;

albo (jednoznaczne):

rec=new structtype;
*(rec).i=0;
*(rec).j=20;
*(rec).k.a=2000000;
*(rec).k.b=400000;
delete rec;

A wracając do wskaźników... :)
Jeden ze sposobów gmatwania...

rec=new structtype;
*(rec+0).i=0;
*(rec+0).j=20;
*(rec+0).k.a=2000000;
*(rec+0).k.b=400000;
delete rec;

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<>b -> (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)

Natomiast pascalowe
a=b

to w C++:
a==b

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:

Var c:byte;
Begin
  If not boolean(c)then - czy c=0
  If boolean(c)then - czy c<>0;
End;

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:

Wrunek w pascalu ogólnie ma postać:

If wartosc_bool
  then
    Begin
    End
  Else
    Begin
    End

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

If(sklep_otwarty and ma_pieniadze)
  Then Ala_kupi_mleko
  Else Ala_pojdzie_do_domu;

W c++

if(wartosc){
  }else{
  }

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

if(sklep_otwarty && ma_pieniadze)Ala_kupi_mleko
  else Ala_pojdzie_do_domu;

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
^ - xor    3 xor 1=2 ... 3^1==2
! - not    trochę inaczej działa :
        dla bajta robi xor $ff (255)
        dla worda xor $ffff (65535)
        dla longinta xor $ffffffff (bduzo-1)

pascalu wrtość 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ć:

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;

Albo prościej:

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;

Taki sam wynik będzie w c++

int i;
for(i=-128;i<=127;i++)
  if(!i)printf("warotść 0 dla i==%d\n",i);

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

Pascal:

Var p:pointer;
P=nil             {nil=pointer(longint(0))}

Longbool(p) -> false

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

void* p;
p=null //rownie dobrze możecie pisac p=0

p -> 0 - oczywiste sam przed chwilą to napisłem.

c)pętle

c++ :
<cpp<
while(wartość){
}
</cpp>
to pascalowe

while(warunek)do
  begin
  end

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:

For indeks:=dol to gora do
  Begin
  End;

Lub

For indeks:=gora downto dol do
  Begin
  End;

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ł, az 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.

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

Type tydzien=(pn,wt,sr,cz,pt,so,nd);
Var i:tydzien;
For i=pn to nd do...

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.

for(ustawienia strtowe;warunek kontnuowania;do wykonania po jednym przejsciu){}

ustawienia startowe:
nie musi być jedna zmienna:
To jest to miejsce, gdzie się przypisuje wartości początkowe.

np.

int i;
long j;
int* p;
for(i=1,j=2,p=null;;)

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.

int* p;
for(p=null;*p==22;)

do wykonania po jednym przejściu :

To jest to, co się wykonuje po wykonaniu jednego przejścia pętli.

int* a=(1,2,3,4,5,6,22); // wskaźnik do tablicy intów
int* p;
for(p=a;*p!=22;p++);

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 oczywiście się, 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:

for(;;){
}

jest pętlą nieskończoną.

  1. Operatory matematyczne/operacje zmiennoprzecinkowe
    a)przypisania

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)
a%=b; -> a=a%b; (mod)

Tylko się nie rozpędźcie a!=b wcale nie znaczy a=a!b – takiego wyrażenia nie ma :-P, pozatym, jeśli cos pominąłem, to już macie niejakie pojęcie o co chodzi.

Te operatory (może poza %) dotyczą wszystkich typów i zmienno i stałoprzecinkowych. Z tym, że operator / dla stało przecinkowych zachowuje się jak div- dzieli bez reszty.

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.

void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(i)dst[i--]=src[i]
}

ta funkcja kopiuje ciąg znaków z src do dst łącznie z kończącym zerem.

void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(i--)dst[i]=src[i]
}

ta kopiuje, ale bez konczącego zera.

void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(--i)dst[i]=src[i]
}

ta kopiuje bez konczącego zera i pierwszego znaku w ciągu.

void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(i)dst[i]=src[i--]
}

void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(i)dst[--i]=src[i]
}

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

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

int i;
i=sin(3.14/3); //pójdzie, ale ... nie ta wartość co trzeba.
  1. Deklaracje zmiennych - once again.

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

for(int i=0;....;..);
int i;

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.

main(){
  if (prawda){
    char* q=&#8217;qrde, gdzie ja jestem?&#8217;;
  }
}

Widziana tylko w bloku if’a

char i;
fun(){
  short j;
}

jedna globalna, druga lokalna.

Itepe, itede , etc. Bardzo szybko załapiecie, gdzie wolno, a gdzie nie wolno deklarować i dlaczego.

  1. Przeciążenia (override)

Tego zdecydowanie nie było w pascalu...

int fun(char* a){
  ... body
}

void fun(integer b,long d){
  .... body
}

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

Ten mechanizm istnieje już w delphi. Jak wiele innych elementów, które wcześniej należały tylko do C++.

Np. komentarze: //

  1. parametry wywołania programu.

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.

C++

main(int argc,char** argv){
}

lub 

main(int argc;char* argv[]){
}

Osobiście nie lubię drugiej formy deklaracji, mimo że jest jak najbardziej poprawna...

Bez przerażenia (czytać od prawej) argv jest to wskaźnik do wskaźnika do znaku. A że wskaźnik do czegos 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.

8.Biblioteki/moduły

Jak się deklaruje użycie modułu w pascalu?

Ano tak:

Uses nazwa_modulu1,nazwa_modulu2; 

A tak w c++ :

#include <nazwa_biblioteki1.h>
#include <nazwa_biblioteki2.h>
#include "nazwa_biblioteki3.h"

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: "" oznaczaja, ż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, choć można w ten sposób dołączać każdy plik.

Tak się mniej więcej przedstawiają odpowiedniki bibliotek:
Mniej więcej, bo funkcje i procedury różnie są porozrzucane.

pascal         c++
system        stdlib,stdio,...
crt               conio
graph          graphics
dos             dir,...

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 i pascalem i że jest to następna produkcja N. Wirtha i nazywa się modula2. To musi być dopiero pokręcony język.

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)

W pascalu też da się tworzyć stałe typu pointer:

C++:

const void far* ekran=(void far*)0xa0000000L;

pascal:

const ekran:pointer=pointer(longint($a0000000));
const nil=pointer(longint(0));

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:

Uwaga to jest przykład dla kompilatora dosowego!!!!

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.

n-ty bit to: bajt n div 8 bit n mod 8

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.

  1. O czym nie wspomniałem? O np. obiektach. O parametrach domyślnych funkcji w C++, o zmiennej ilości parametrów wywołania funkcji, czego nie ma w pascalu. Chociaż mozna napisać tak program, że nie będzie to konieczne. ...O makrach. O constach w c++ - na ten temat możnaby wiele.

10 komentarzy

Co do include i uses to właśnie o to mi chodziło, byś uzupełnił dla pełności opis tego. Ja wiem, że ty wiesz, ale czytający mogą nie wiedzieć :)
Co do Delphi to wciąż uważam, że najlepiej w ogóle nie wspominać niż wspominać tylko cząstkowo, bo czytelnik może uznać, że tylko do tego ograniczają się różnice pomiędzy Delphi a Pascalem.
Jeżeli chodzi o semantyki i inne... przepraszam, że tak naskoczyłem, ale kompilatory to mój aktualny konik ;)

TLiczba = (malo=10, srednio=50, duzo=100, bduzo)
To prezejdzie w delphi, nie w pascalu.
2.
...Spojrzałem w otchłań i obróciłem przerażony,
przecież nie z każdym potworem mogę się rozprawić... :)
3.
Nie oglądam Idola, bo osobiście uważam to za dno.
Pozatym nie mam czasu na telewizornię.
4.
Sematyka... Owszem nazwa może i nie ta, ale ja
jestem na polibudize, a nie na polonistyce.
5.
Co do porównania bibliotek i unitów, to
używałem słów podobny i mniej więcej.
Doskonale wiem jakie są róźnice i dlaczego.
Uses i include - nie z każdym potworem walczę.
Nie muszę, nawet jesli komuś wydaje sie inaczej.
A działanie mają bardzo podobne. Mają za zadanie
spowodować, dołączenie procedur, funkcji, zmiennych,
typów ... itepe.
6.
Powtórzę się: override nie ma w pascalu, a w nagłówku
napisałem, że jeżeli będe pisał o Delphi, to dam to
jasno do zrozumienia.
7.
To samo dotyczy wskaźników, ni wspominałem tam o Delpji.
8.
Może faktycznie nie do konca jest z tym przerzucaniem
kodu w c, o czyms zapomniałem faktycznie.
9.
command.com - możemy sie sprzeczać całe lata...
(w zależności od wersji dosa, obaj będziemy mieli rację)
ente...
Łatwiej skrytykować, niż samemu stworzyć...

Ci co mają, nie potrzebują więcej.
Ci co potrzebują nie mają...

Sorki za formatownie, nie mioałem czasu. Jesli jest jeszcze mozliwość chętnie to zrobię. W razie czego adres podałem. Dżizas jak ja nie lubię html'a. Naprawdę ładnie wygląda, ale brak mu wielu rzeczy (np. void*)

Mam tylko jedmo zastrzeżenie, rozdział 2.e: Pascal też umożliwia konstrukcje typu
TLiczba = (malo=10, srednio=50, duzo=100, bduzo) // i bduzo ma 101

Poza tym, wszystko super (no, może jeszcze przypomnę o tagach < delphi > i < cpp > - KONIECZNIE je wstaw). Paru ciekawych rzeczy o C++ się dowiedziałem (chociaż większości się nauczyłem podczas początków z.... PHP :) )

Wprost uwielbiam ten kawałek filozofii:
... Kiedy walczysz z potworami, uważaj, żebyś sam nie stał się jednym z nich...
... Kiedy spoglądasz w otchłań, ona również patrzy na ciebie...

Jak tak dalej pójdzie to będę musiał wyzbyć sie całego zapasu National Geographic i wtedy ja sam zacznę narzekać na brak formatowania.

Będę twardo bronił stanowiska - ten Art mi się podoba. Oczywiście jego temat jest tak rozległy, że już z samego założenia muszą tu być jakieś niedopowiedzenia, przekłamania. Ale naprawdę sporo się nauczyłem odnośnie pascala.

Ratunku.

Kapustka: wg mnie to Lofix jest tym dla 4p co Wojewódzki dla 'idola'. to Lofix wymiata perfidnie lolków :P, to na Lofixa najczęsciej narzekają (oczywiście niesłusznie ;) to Lofixowi wysyłają maile z pogróżkami :)

  1. Semantyka - to co opisujesz to tylko i wyłącznie syntaktyka. Opisujesz składnię, nie znaczenie. A w szczególności pisząc o wielkich i małych literach zahaczasz o leksykę.
  2. Opis działania kompilatora C jest przezabawny :) Preprocesor nie robi nic ponad operowanie na znakach, więc jak możemy mówić tu w ogóle o semantyce, jak tu nawet składnia ogranicza się jedynie poleceń preprocesora? Sprawdzanie "błędów ortograficznych" to już zadanie analizatora składniowego (czyli syntaktyka wchodzi w grę).
  3. uses i #include mają kompletnie inne działanie, dobrze byłoby wytłumaczyć różnicę pomiędzy nimi i przy okazji wspomnieć o zakresie działania zmiennych w C (niwelujących wady include).
  4. Lepiej nie wspominaj o Delphi w tym porównaniu, bo Delphi od Pascala różni się w bardzo dużym stopniu. Chociażby type może inaczej działać. Jeżeli chodzi o override to w Delphi można zastosować od dowolnych funkcji. A przy typie Variant także operatory. Również inaczej można się odwoływać do struktur wskazywanych przez wskaźniki niż tylko przez '^.'
    Ale najważniejszą rzecz wspomiał już Marooned

/* Dopisane: Kapustka: to chyba i ja będę potrzebował, bo jak dla mnie też nie jest czytelny :P */

ekhm, Kapustka wybył na pocztę - spodziewam się na dniach partii NG [rotfl] - szalony to człowiek... ;)

Nasz drogi Marooned jest tym dla 4p, czym Kuba Wojewódzki dla idola. Sądzę, że pomylił 4p z "National Geographic". Twój art jest całkowicie czytelny.

Błędy zdarzają się każdemu i nie są rzeczą złą - to właśnie na nich najlepiej się uczymy (obydwie strony). Prawda? - ona zazwyczaj nie skłania do refleksji, zjadamy ją jak płatki śniadaniowe. Jeśli ktoś sądzi inaczej to jestem gotów podjąć dyskusję na gg: 3499638.

Wydaje mi się że taki artykuł już kiedyś czytałem - "Algorytmy i struktury danych" wyd 2 Helion. Nie jestem w stanie tego teraz zweryfikować, ale jeśli taki rozdział się tam znajduje to jest on w suplemencie.

Tylko że Ty zrobiłeś to o wiele dogłębniej i ciekawiej.

Pozdrawiam
Ela Zapendowska.

ps. wysłałem już do Marooneda kilka egzemplarzy "National Geographic", więc prawdopodobnie w najbliższym czasie przestanie narzekać.

Brak formatowania (zarówno wcięcia, jak i tagi <cpp> i <delphi>) połączone z tą niesamowitą długością artykułu sprawia, że jest ona bardzo nieczytelny.

Ponadto częste literówki, ort. w samym tytule i stwierdzenie, że plik command.com jest plikiem typu .com - to zwykły exe ze zmienionym rozszerzeniem.