ZMIENNE.
Przykłady zastosowania i sposoby konwersji zmiennych jednego typu do zmiennych innego typu.
Jak skonwertować tekst na liczbę i odwrotnie.
Sposoby konwersji tekstu na liczby zostały opisane w dziale Łańcuchy Znaków, więc nie będę tutaj tego wszystkiego przepisywał, podam jedynie opis najbardziej przydatnych (moim zdaniem) metod klasy AnsiString przeznaczonych do konwersji pomiędzy różnymi typami zmiennych.
| ToInt() | - |
metoda konwertująca zmienną typu String na int, np.: int i = Edit1->Text.ToInt(); Przy czym obiekt Edit1 w treści musi zawierać tekst reprezentujący liczbę całkowitą. |
| ToIntDef() | - |
metoda konwertująca zmienną typu String na int z tą jednak różnicą, że jeśli konwersja się nie powiedzie to zwracana jest wartość domyślna podana w nawiasie, np.: |
| ToDouble() | - |
metoda konwertująca zmienną typu String na double lub float, np.: |
| CurrToStr | - |
metoda konwertująca zarówno zmienne int, float, double lub String na zmienną String, np.: |
| CurrToStrF | - |
ta metoda działa podobnie jak CurrToStr z tą jednak różnicą, że pozwala formatować konwertowaną wartość na kilka sposobów, np.: |
|
FloatToStr Poprawiono błędny opis funkcji. | - |
zasada stosowania jest podobna do tej stosowanej w przypadku CurrToStr. Funkcji używa się do konwersji liczb dziesiętnych na tekst, czyli zmiennych typu float lub double na zmienne typu String, np.: |
|
FloatToStrF Poprawiono błędny opis funkcji. | - |
zasada stosowania jest podobna do tej stosowanej w przypadku CurrToStrF.
Funkcji używa się do konwersji liczb dziesiętnych na tekst, czyli zmiennych typu float lub double na zmienne typu String. Zasada stosowania specyfikatora formatów jest identyczna jak w przypadku funkcji CurrToStrF, np.: |
| StrToInt | - |
metoda konwertująca tekst będący reprezentacją liczby całkowitej na liczbę całkowitą: int x = StrToInt(Edit1->Text); Uzyskany efekt będzie taki sam jak w przypadku metody ToInt(). |
| StrToIntDef | - |
metoda konwertująca zmienną typu String na int z tą jednak różnicą, że jeśli konwersja się nie powiedzie to zwracana jest wartość domyślna podana w nawiasie, np.: int i = StrToIntDef(Edit1->Text, 10); Przy czym obiekt Edit1 w treści powinien zawierać tekst reprezentujący liczbę całkowitą, w przeciwnym razie zmiennej i zostanie przypisana wartość domyślna, w przykładzie będzie to liczba 10. Jako wartość domyślną można podawać tylko liczby. Uzyskany efekt będzie taki sam jak w przypadku metody ToIntDef(). |
| StrToFloat | - |
metoda konwertująca zmienną typu String na double lub float, np.: float i = StrToFloat(Edit1->Text); Przy czym obiekt Edit1 w treści musi zawierać tekst reprezentujący liczbę całkowitą lub dziesiętną. Jeżeli jest to liczba zmiennopozycyjna (ułamek dziesiętny) to część całkowita od dziesiętnej musi być rozdzielona przecinkiem a nie kropką, np.: 100,25. Tutaj występuje pewna rozbieżność, mianowicie jeżeli wprowadzamy liczbę dziesiętną bezpośrednio w kodzie programu to rozdzielamy część całkowitą od dziesiętnej kropką. Inaczej dzieje się jednak gdy już uruchomimy nasz program i wprowadzamy np. liczbę do obiektu Edit1 wtedy liczba dziesiętna powinna być rozdzielana przecinkiem. Uzyskany efekt będzie taki sam jak w przypadku metody ToDouble(). |
Jak można się domyślić typ wariantowy to taki rodzaj zmiennej, której można przypisać dowolną wartość, czyli może to być zarówno liczba lub znak, jak również łańcuch znaków, wskaźnik odwołanie lub tablica. Typ wariantowy ma zastosowanie wtedy gdy potrzebujemy zmiennej o typie modyfikowanym w czasie wykonywania programu. Żeby to dokładniej wyjaśnić posłużę się przykładem, tak więc umieszczamy na formularzu cztery komponenty Edit1, Edit2, Edit3 i Button4 następnie tworzymy w pliku źródłowym prostą funkcję, którą nazwę Oblicz. Funkcja ta będzie dodawała do siebie dwie wartości typu Variant a następnie będzie zwracała wartość również typu Variant:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //-------------------------------- Variant Oblicz(Variant x, Variant y) { return x + y; } //-------------------------------- |
Teraz w zdarzeniu OnClick dla przycisku Button1 wywołujemy funkcję Oblicz. Najpierw wywołamy funkcję przekazując jej jako argumenty wartości typu String:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //-------------------------------- Variant Oblicz(Variant x, Variant y) { return x + y; } //-------------------------------- void __fastcall TForm1::Button1Click(TObject* Sender) { Edit3->Text = VarToStr( Oblicz(Edit1->Text, Edit2->Text) ); } //-------------------------------- |
W podanym przykładzie niezależnie od tego czy w właściwości Text obiektów Edit1 i Edit2 będzie łańcuch znaków czy liczba, funkcja oblicz "połączy" ze sobą de dwie wartości, żeby dokonać sumowania dwóch liczb trzeba wywołać funkcję Oblicz z wartościami liczbowymi, czyli np. z wartościami typu int, float, double itp.:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //-------------------------------- Variant Oblicz(Variant x, Variant y) { return x + y; } //-------------------------------- void __fastcall TForm1::Button1Click(TObject* Sender) { Edit3->Text = VarToStr( Oblicz(Edit1->Text.ToDouble(), Edit2->Text.ToDouble()) ); } //-------------------------------- |
Tak wywołana funkcja zsumuje argumenty i zwróci wynik, jednak w tym przypadku właściwość Text obiektów Edit1 i Edit2 musi zawierać wartości reprezentujące liczby.
Reasumując zmienna typu Variant zezwala na pewną niedbałość i umożliwia tworzenie jednego typu zmiennych dla wartości różnych typów, eliminuje to konieczność ustalania z góry konwersji zmiennej jednego typu na drugi, ponieważ wszystkim zajmie się kompilator.
O tym, że pisząc kod
programu należy nadawać zmiennym różne nazwy wiedzą zapewne wszyscy, czyli
taki kod jest
błędny:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- String a = "Globalna"; void __fastcall TForm1::Button1Click(TObject *Sender) { String a = "Lokalna"; Edit1->Text = a + a; } //-------------------------------- |
Przedstawiony kod jest błędny ponieważ utworzyłem dwie zmienne typu
AnsiString, jedną globalną i jedną lokalną i obydwie mają taką samą nazwę, co w
C++ jest niedopuszczalne, z tego względu, że kompilator w takiej sytuacji nie
wiem o którą zmienną chodzi i w takiej sytuacji zawsze użyje zmiennej lokalnej
pomijając zupełnie zmienną globalną, dlatego ten kod powinien wyglądać
następująco:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- String a = "Globalna"; void __fastcall TForm1::Button1Click(TObject *Sender) { String b = "Lokalna"; Edit1->Text = a + b; } //-------------------------------- |
Taki kod jest już poprawny.
Teraz pewnie niektórych zaskoczę, ale można używać zmiennych tych samych lub
różnych typów o takich samych nazwach, trzeba je tylko przysłaniać, a do
przysłaniania zmiennych służy podwójny dwukropek:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- String a = "Globalna"; void __fastcall TForm1::Button1Click(TObject *Sender) { String a = "Lokalna"; Edit1->Text = ::a + a; } //-------------------------------- |
lub:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- String a; void __fastcall TForm1::Button1Click(TObject *Sender) { ::a = "Globalna"; String a = "Lokalna"; Edit1->Text = ::a + a; } //-------------------------------- |
I tak to właśnie wygląda. Obowiązują pewne zasady, po pierwsze można zadeklarować tylko dwie zmienne o takiej samej nazwie, a po drugie jedna zmienna musi być lokalna, a druga globalna, po trzecie podwójny dwukropek zawsze odnosi się do zmiennej globalnej. Jaki jest sens stosowania zmiennych o takich samych nazwach - nie wiem.
Konwersja liczb arabskich na rzymskie.
Nigdy nie
potrzebowałem dokonywać konwersji liczb arabskich na rzymskie, no ale nigdy nie
wiadomo, kiedy to będzie potrzebne, dlatego przedstawiam tutaj prostą funkcję,
ale skutecznie konwertującą liczby arabskie na rzymskie. W poradzie posłużę się
obiektem Edit1 w którym zostaną wyświetlone liczby rzymski oraz obiektem
CSpinEdit1 z zakładki Samples, który będzie podawał liczbę do konwersji:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- AnsiString ArabicToRoman(int arabic) { unsigned short int n[] = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000}; AnsiString r[] = {"I", "IV", "V", "IX", "X", "XL", "L", "XC", "C", "CD", "D", "CM", "M"}; AnsiString roman = ""; int i = (sizeof(n) / sizeof(n[0])) - 1;; do { while(arabic >= n[i]) { arabic -= n[i]; roman += r[i]; } --i; } while(i >= 0); return roman; } //-------------------------------- void __fastcall TForm1::CSpinEdit1Change(TObject *Sender) { Edit2->Text = ArabicToRoman(CSpinEdit1->Value); } //-------------------------------- |
Konwersja liczby zmiennopozycyjnej na łańcuch znaków.
W bibliotece stdlib.h istnieje funkcja umożliwiająca konwersję liczby zmiennopozycyjnej na łańcuch znaków:
char *ecvt(double value, int ndig, int *dec, int *sign);
Przedstawiona funkcja działa jednak zupełnie inaczej niż np. funkcja
FloatToStr, czy FloatToStrF. Funkcja ecvt w odróżnieniu od
innych przerabia liczbę zmiennopozycyjną na łańcuch znaków, co w istocie
powoduje usunięcie przecinka i przedstawienie liczby w postaci wartości
dziesiętnej z określoną przez parametr ndig liczbą cyfr, czyli mamy na
przykład liczbę zmiennopozycyjną 9.876 (value = 9.876) i określamy w
parametrze ndig, że długość łańcucha ma wynosić 10, w efekcie po
konwersji otrzymamy wynik 9876000000. Oprócz tych dwóch parametrów funkcja
podaje jeszcze informację o liczbie cyfr w wartości dziesiętnej liczby
zmiennopozycyjnej - parametr dec, czyli jeśli mamy np. liczbę
zmiennopozycyjną 143567.6789 to dec przyjmie wartość 6 bo 143567.- gdzie
przy obliczaniu tej wartości cyfry po przecinku nie są brane pod uwagę z jednym
wyjątkiem, jeżeli jako parametr value podamy liczbę zmiennopozycyjną w
zapisie wykładniczym, to przy obliczaniu parametru dec brane
są pod uwagę zarówno liczby przed jak i po przecinku. Ostatni parametr sign
określa czy mamy do czynienia z liczbą dodatnią - przyjmie wartość 0, czy z
liczbą ujemną - przyjmie wartość 1.
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <stdlib.h> #include <stdio.h> void __fastcall TForm1::Button1Click(TObject *Sender) { char *string; double value; int dec, sign; int ndig = 10; char Buf[288]; value = 9.876; string = ecvt(value, ndig, &dec, &sign); sprintf(Buf, "string = %s dec = %d sign = %d", string, dec, sign); Edit1->Text = (String)Buf; value = -123.45; ndig = 15; string = ecvt(value,ndig,&dec,&sign); sprintf(Buf, "string = %s dec = %d sign = %d", string, dec, sign); Edit2->Text = (String)Buf; value = 0.6789e5; // Zapis wykładniczy ndig = 5; string = ecvt(value,ndig,&dec,&sign); sprintf(Buf, "string = %s dec = %d sign = %d", string, dec, sign); Edit3->Text = (String)Buf; } //-------------------------------- |
Podział liczby zmiennopozycyjnej na cześć całkowitą i cześć ułamkową.
W bibliotece math.h znajdują się funkcje modf (dla liczb typu double) i modfl (dla liczb typu long double) umożliwiające rozbicie liczby zmiennopozycyjnej na dwie części, czyli na cześć całkowitą i cześć ułamkową.
double modf(double
x, double *ipart);
long double modfl(long double x, long double *ipart);
Funkcje są proste w użyciu i nie wymagają jakiegoś szczególnego opisu.
Przedstawię tutaj cztery różne przykłady wykorzystania funkcji modf.
Operacje na funkcji modfl wyglądają tak samo z tą różnicą, że
należy stosować zmienne long double:
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <stdio.h> #include <math.h> void __fastcall TForm1::Button1Click(TObject *Sender) { double fraction, integer; double number = 100000.567; char Buf[255]; fraction = modf(number, &integer); sprintf(Buf, "Liczba: %lf zawiera całość: %lf i część ułamkową: %lf", number, integer, fraction); Edit1->Text = (String)Buf; } //-------------------------------- |
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <math.h> void __fastcall TForm1::Button1Click(TObject *Sender) { double fraction, integer; double number = 100013.567; char Buf[255]; fraction = modf(number, &integer); Edit1->Text = Format("Cześć całkowita: %.f", ARRAYOFCONST((integer))); Edit2->Text = Format("Cześć ułamkowa: %.3f", ARRAYOFCONST((fraction))); } //-------------------------------- |
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <math.h> void __fastcall TForm1::Button1Click(TObject *Sender) { double fraction, integer; double number = 100013.567; char Buf[255]; fraction = modf(number, &integer); TVarRec vr[] = {(int)integer, fraction}; Edit1->Text = Format("Cześć całkowita: %d część ułamkowa: %.3f", vr, sizeof(vr)); } //-------------------------------- |
| // Plik źródłowy np. Unit1.cpp //-------------------------------- #include <math.h> void __fastcall TForm1::Button1Click(TObject *Sender) { double fraction, integer; double number = 100013.567; char Buf[255]; fraction = modf(number, &integer); Edit1->Text = "Cześć całkowita: " + (String)integer; Edit2->Text = "Cześć ułamkowa: " + FloatToStrF(fraction, ffNumber, 7, 3); } //-------------------------------- |