Tak jak w Javie czy Pascalu, obowiązuje w C/C++ zasada ścisłej kontroli typu. Wszystkie występujące w programie zmienne muszą być przed ich pierwszym użyciem zadeklarowane i zdefiniowane. O ile jednak np. w Javie deklaracja zmiennej jest zawsze jednocześnie jej definicją (to znaczy wiąże się z przydzieleniem tej zmiennej pamięci), o tyle w C/C++ deklaracja nie zawsze oznacza definicję — zmienną definiować, czyli przydzielać jej miejsce w pamięci można oczywiście tylko raz, ale deklarować wielokrotnie. Jak to możliwe, wyjaśnimy bardziej szczegółowo w rozdziale na temat zmiennych zewnętrznych . Samą zmienną możemy uważać za nazwane miejsce w pamięci, w którym przechowywana jest wartość pewnego znanego kompilatorowi typu. Kompilator potrzebuje tej informacji, aby zarezerwować na zmienną odpowiednią ilość pamięci i aby wiedzieć, jak interpretować różne operacje na tej zmiennej.
Zasady poprawności identyfikatorów (nazw) zmiennych są w C/C++ podobne jak w innych językach: identyfikatory mogą składać się z liter, cyfr i znaku podkreślenia; nie mogą rozpoczynać się cyfrą (w odróżnieniu od niektórych innych języków, znaki waluty nie są w identyfikatorach dozwolone).
Tak więc nazwy (identyfikatory) A_book i a_book będą traktowane jako różne.
Wbudowane typy podstawowe są podobne do tych, jakie być może Czytelnik zna z Javy. Występują jednak pewne różnice. W szczególności, nie jest gwarantowana stała długość (w bajtach) reprezentacji maszynowej zmiennych poszczególnych typów. Na przykład, zmienne typu int mogą mieć rozmiar, w zależności od implementacji, 2, 4 lub 8 bajtów (obecnie jednak prawie zawsze są to cztery bajty). W związku z tym istnieje przydatny, wbudowany operator sizeof, który zwraca długość reprezentacji binarnej zmiennej danego typu na danej platformie (w bajtach). Użyć tego operatora możemy tak jak funkcji, której jedynym argumentem jest albo dowolna zmienna typu, dla którego chcemy poznać długość reprezentacji, albo nazwa samego typu (w pierwszym z tych przypadków nawias jest opcjonalny). Podanie nazwy typu wystarczy, gdyż
Jak powiedzieliśmy, wszystkie zmienne — nazwane
obszary pamięci o określonym adresie i typie — musza być przed
pierwszym użyciem zadeklarowane i zdefiniowane. Jak to zrobić, pokażmy
na przykładzie zmiennych typu
int:
1. #include <iostream> 2. using namespace std; 3. 4. int main() { 5. int k1; 6. int k2(); 7. int k3(1); 8. int k4{}; 9. int k5{1}; 10. int n=1, m = n, i{1}, j{i}; 11. }
Jak widać, podajemy najpierw nazwę typu, a potem nazwę definiowanej zmiennej. Nie musimy, ale raczej powinniśmy, nadać nowowprowadzonej zmiennej jakąś sensowną wartość. Robimy to poprzez zainicjowanie zmiennej od razu w miejscu definicji. Na przykład w powyższym programie:
int n = 1; int m = n; int i{1}; int j{i};z czego widać, że, na przykład, definiując m możemy traktować n jako już istniejącą zmienną.
1. #include <iostream> 2. #include <string> 3. using namespace std; 4. 5. int main() { 6. long double ld = 0; 7. string st = "Hermenegilda"; 8. short sh = 0; 9. long *lo = 0; 10. cout << "long double: " << sizeof ld << endl 11. << "double : " << sizeof(double) << endl 12. << "float : " << sizeof(float) << endl 13. << "long long : " << sizeof(long long) << endl 14. << "long : " << sizeof(long) << endl 15. << "int : " << sizeof(int) << endl 16. << "short : " << sizeof sh << endl 17. << "char : " << sizeof(char) << endl 18. << "bool : " << sizeof(bool) << endl 19. << "string : " << sizeof st << endl 20. << "long* : " << sizeof lo << endl; 21. }
dał wyniki następujące ma maszynie 32-bitowej
long double: 12 double : 8 float : 4 long long : 8 long : 4 int : 4 short : 2 char : 1 bool : 1 string : 4 long* : 4Widać, że w tym systemie typ long ma tę samą reprezentację co int (tak zwykle jest na maszynach 32-bitowych).
Ten sam program na maszynie 64-bitowej może wypisać
long double: 16 double : 8 float : 4 long long : 8 long : 8 int : 4 short : 2 char : 1 bool : 1 string : 8 long* : 8Tu, jak widać, typ long ma tę sama reprezentację co long long.
Widać też, że w obu przypadkach zmienna typu string ma tyle bajtów, ile wskaźnik (na przykład long* w ostatnim wierszu) — a więc prawdopodobnie jest wskaźnikiem (choć pewnie nie zwykłym, a „inteligentnym”).
Jak widać, deklaracja typu ma postać
Typ zmienna;choć, w C++11 może to być też
auto zmienna = wartość;lub
decltype(wyrażenie) zmienna;Słowo kluczowe auto znaczy tu, że kompilator sam ma się domyślić, patrząc na wartość inicjującą, jaki ma być typ deklarowanej/definiowanej zmiennej (oczywiście typ ten będzie ściśle ustalony i nie może być potem zmieniony). Z kolei decltype znaczy, że zmienna ma być tego typu, co wartość wyrażenia podanego w nawiasie. Przykład powinien wyjaśnić sposób, w jaki sposób obie konstrukcje mogą być zastosowane:
1. #include <iostream> 2. using namespace std; 3. 4. int main() { 5. auto k = 7; // k jest typu int 6. auto x = 1.; // x jest typu double 7. decltype(x) y = 7; // y jest typu double, choć 8. // '7' jest literałem typu int 9. decltype(k*x) z = 7; // iloczyn k*x jest typu double 10. cout << "k/2=" << k/2 << ", y/2=" << y/2 11. << ", z/2=" << z/2 << endl; 12. }
Programik ten drukuje
k/2=3, y/2=3.5, z/2=3.5co pokazuje, że rzeczywiście y i z są typu double — gdyby były typu int, to dzielenie przez 2 dałoby wynik dokładnie 3 (tak jak to jest w przypadku k)
Przydatność tych konstrukcji może się na tym etapie wydawać wątpliwa, ale przekonamy się, że są one niezwykle przydatne!
T.R. Werner, 21 lutego 2016; 20:17