Artykuły do poprawy

Pascal i C - czyli tam i z powrotem- część pierwsza poprawiona

Ten artykuł wymaga dopracowania!

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:

 



C++ i pascal, czyli tam i z powrotem


Spis tresci

  1. Wstęp
  2. Semantyka
  3. Typy zmiennych
  4. Porównania, warunki, pętle
  5. Operatory, wyrażenia
  6. Deklaracje ponownie
  7. Przeciążania
  8. Parametry wywołania programu
  9. Biblioteki/moduły
  10. Ciekawostki
  11. 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:


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


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 :


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


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.


lub zapis jednoznaczny:


pascal:


lub zapis jednoznaczny:


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:


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>

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


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


Chcemy zadeklarować procedurę proc, która wypisuje roż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ć. ;-)</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.


e)parametry wywołań funkcji/procedur

Zacznę od przykładu (od nagłówków jednakowych procedur):




Rozumiecie?</p>

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

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





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



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>

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


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



(np. tak zwraca pointer procedura getmem(var p:pointer,...))
A w mniej chorym zapisie:


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


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


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:


ALBO jednoznaczne


c++ :


LUB jednoznaczne

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

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




lub (jednoznaczne)


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.


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.


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:


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


tablica ma 2 komórki.

odwołania do tablicy:


Tablica dwuwymiarowa:


odwołanie:


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


można zadeklarować np. typ wyliczeniowy:


i tablicę :


albo:


I tak dalej, i tak dalej na wszelkie możliwe sposoby.</p>

C++ : Tablice są bardziej ograniczone:
Deklaracja wygląda następująco np.:


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


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:


przypisanie :

</p>

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

Pascal:


C++ :


albo:


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


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:


to otrzymamy 0</p>

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


lub


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:


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.


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:


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

</p>

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


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.


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


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:


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.


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


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


g)rekordy/struktury

record/struct, niezależnie od tego jak się nazywa, chodzi o to samo. Deklaracje typów
Pascal:


C++


lub (jednozanczne)


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

h)deklaracje i odwołania do zmiennych:
pascal:


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


lub deklaracja samej zmiennej rec, bez deklaracji typu.


odwołania:
c++:


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


Ale pascal ma też wielkie ułatwienie:


Albo:



Odwołania do zmiennych tworzonych dynamicznie.
Załóżmy ...
Pascal:


C++ :


albo (jednoznaczne):


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


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)


Natomiast pascalowe

a=b
</td></table>
to w C++:
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:

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

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

If(sklep_otwarty and ma_pieniadze)
  Then Ala_kupi_mleko
  Else Ala_pojdzie_do_domu;
</td></table>
W c++

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

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
- xor    3 xor 1=2 ... 31==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ć:

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:

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

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:

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

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

while(wartość){
}
</td></table>
to pascalowe

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:

For indeks:=dol to gora do
  Begin
  End;
</td></table>
Lub

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.

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.

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.

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

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:

for(;;){
}
</td></table>
jest pętlą nieskończoną.</p>

4. 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 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="shl" ...="..." jak="jak" wyżej.="wyżej." a="a">>=b;-> a=a>>b; // shr ...

{a te tylko dla stałoprzecinkowych)


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.


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


ta kopiuje, ale bez konczącego zera.

&lt;table width="100%" bgcolor="black" cellpadding="8">
void strcpy(char* src,char* dst){
  int i;
  i=length(src);
  while(--i)dst[i]=src[i]
}
</table>
ta kopiuje bez konczącego zera i pierwszego znaku w ciągu.




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




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


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.


Widziana tylko w bloku if'a


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)


Tego zdecydowanie nie było w pascalu...


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>

Ten mechanizm istnieje już w delphi. Jak wiele innych elementów, które wcześniej należały tylko do C++. (4programmers.net- thnx)


Np. komentarze: //


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


lub


Osobiście nie lubię drugiej formy deklaracji, mimo że jest jak najbardziej poprawna...</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.

8.Biblioteki/moduły


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


A tak w c++ :


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>

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
</p>

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


pascal:


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



n-ty bit to:  bajt n div 8   bit n mod 8</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.



10. O czym nie wspomniałem?


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




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

2. Konwersje typów (rzutowanie typów)

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 (*

10 komentarzy

Patrol27 2009-10-28 20:21

100 na 10 punktów !

Dominium 2008-06-11 08:05

Hmm, chyba cos miejscami style się zepsuły.. :/

mucha87 2006-07-15 00:24

Bardzo dobry artykul, czy bedzie kiedys dokonczony?

ŁF 2006-06-08 19:31

tia, formatowanie tekstu działające tylko pod IE. pod innymi przeglądarkami tekst jest nieczytelny.

Dr Prozac 2006-06-06 20:41

Artykuł zupelnie nieczytelny. Jakies problemy z kodowaniem ("&plusmn" 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.

Ludomir 2006-02-16 23:03

bardzo... trudno sie go czyta :/ :/ :/

skalniak 2004-08-31 11:07

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 ?

RafalS 2004-08-03 10:28

Bardzo wartościowy artykuł !!!

RudyBestyj 2008-06-12 14:01

Dobra robota manfredek  [browar]