czwartek, 19 grudnia 2013

Co nie co o typach w C++

Definiując daną nazwę mówimy kompilatorowi aby zarezerwował w pamięci miejsce na obiekt danego typu o podanej nazwie.

Różnica między definicją a deklaracją jest taka, iż deklaracja mówi kompilatorowi jaka nazwa reprezentuje dany obiekt konkretnego typu bez rezerwacji miejsca w pamięci, a definicja dodatkowo rezerwuje miejsce w pamięci (powołanie do życia). Przykład:
int zmienna1; // Definicja z deklaracją.
extern int zmienna2; // Deklaracja.

Typ wchar_t może przechowywać rozszerzony zestaw znaków alfanumerycznych.

Zestawienie typów wraz z ich szerokością, zakresem wartości oraz przykładem literału:
- char: 1 bajt , -128-127, 'a' czy też '\x61',
- unsigned char: 1 bajt, 0-255, '!',
- wchar_t: 2 bajty, 0-65535, L'A' czy też L"abcd",
- short: 2 bajty, -32768-32767, -1
- unsigned short: 2 bajty, 0-65535, 2
- int: 4 bajty, -2147483648-2147483647, 0x9FE,
- unsigned int: 4 bajty, 0-4294967295, 10U,
- long: 4 bajty, -2147483648-2147483647, -77L,
- unsigned long: 4 bajty, 0-4294967295, 5UL,
- float: 4 bajty, 3,4e-38-3,4e38, 3.14f
- double: 8 bajtów, 1,7e-308-1,7e308, 1.234,
- long double: 10 bajtów, 3,4e-4932-1,1e4932, 1.234L.
Typ int ma w tym zestawieniu tą samą szerokość co typ long (kompilatory 32 bitowe). Jednak jeżeli kompilator będzie np.16 bitowy to zakres wartości może być mniejszy. Krótko mówiąc; sposób przechowywania liczby może zależeć od typu kompilatora. Możemy to sprawdzić używając operatora sizeof. Stałe całkowite traktuje się jako typ int (np. 1 i 100) natomiast w przypadku bardzo dużych liczb jest to typ long. Z kolei stałe zmiennoprzecinkowe (np. 12.5, 8e2) traktuje się jako typ double.

Niejednokrotnie nasuwa się pytanie dlaczego utrudniać sobie życie i pisać coś w systemach innych niż dziesiątkowy. Niekiedy stosowanie innego zapisu ułatwia nam właśnie pracę np. podczas ustawiania bitów w komórce pamięci. Biorąc pod uwagę ten przykład to przy użyciu zapisu szesnastkowego łatwo wyobrazić sobie pozycje żądanych bitów. Przykładowo jeżeli chodzi o ustawienie samego bitu 9 i 10 to można to zapisać jako "zmienna = 0x300;". Innym argumentem za stosowaniem systemu innego niż dziesiętny jest to, że komputery rozumieją tylko system binarny.

Dlaczego lepiej używać przydomka "const" zamiast dyrektywy "#define"? Używając #define sprawiamy, że kompilator nie jest w stanie skojarzyć co dana nazwa oznacza ponieważ operuje tylko na tekście. Ponadto kompilator nie może sprawdzić czy dana nazwa została użyta w ramach jej zakresu ważności (zakres taki nie występuje). Tym samym nie mamy możliwości śledzenia za pomocą debuggera jak dany obiekt się zmienia.

Przydomek "register" dodany do definicji obiektu mówi kompilatorowi, iż chcemy bardzo często używać tego obiektu i tym samym mieć do niego szybki dostęp. Sprawi to, że zmienna zostanie umieszczona w rejestrze, z którego może być odczytana bardzo szybko. Kompilator jednak może tego nie uwzględnić. Z uwagi na to, że obiekt jest umieszczany w rejestrze to nie możemy dowiedzieć się pod jakim adresem jest przechowywany. Jeżeli spróbujemy to zrobić to zmienna zostanie umieszczona w zwykłej pamięci.

Z kolei specyfikator "volatile" nakazuje ostrożność w posługiwaniu się daną zmienną. Jeżeli nasz obiekt może się w jakiś niezauważalny sposób zmieniać to użycie tego przydomka jest dobrym pomysłem. Kompilator nie będzie wtedy szedł na skróty tylko za każdym razem sprawdzi czy odwołuje się do odpowiedniej komórki pamięci.

Jeżeli chcemy dodać jakąś nazwę do konkretnych wartości opisujących np. oceny to warto jest skorzystać z typu wyliczeniowego enum. Przykład:
#include <iostream>

using namespace std;

int main() {
    enum grades {
        insufficient = 1,
        allowing = 2,
        sufficient = 3,
        good = 4,
        very_good = 5,
        excellent = 6
    };

    enum numbers {
        one, // 0
        two = 5,
        three, // 6
        four = (three + 4) // 10
    };

    enum {
        first = 1,
        second,
        third = 5
    };

    grades primary_school; // Definicja zmiennej, której można podstawić tylko jedną z określonych na liście wyliczeniowej wartości.

    primary_school = allowing;

    primary_school = good;

    cout << primary_school << endl;

    cout << good << endl;

    cout << four << endl;

    cout << second << endl;
}

Źródło: Grębosz J., Symfonia C++, EDITION 2000, 2008

środa, 11 grudnia 2013

Kod C++ - histogram

// Program, który rysuje histogram wystąpień dużych liter w danym pliku tekstowym.

#include <iostream>
#include <fstream>
#include <string>
#include <conio.h>
#include <stdlib.h>

using namespace std;

int main() {
    string filename; // Nazwa pliku, z którego program ma czytać.
    ifstream stream; // Definicja strumienia do pracy z plikiem.
    char alphabet[25]; // Licznik wystąpień znaków.

    // Zerowanie licznika.

    for (int iterator = 0; iterator < 25; iterator++)
        alphabet[iterator] = 0;

    cout << "Podaj sciezke do pliku: ";

    cin >> filename;

    stream.open(filename.c_str(), ios::out);

    if (!stream) {
        cout << "Nie udalo sie otworzyc podanego pliku.";

        exit(-1);
    }

    char actual; // Aktualny znak z pliku wejściowego.

    try {
        while (stream.get(actual)) {
            switch (actual) {
            case 'A':
                alphabet[0]++;

                break;

            case 'B':
                alphabet[1]++;

                break;

            case 'C':
                alphabet[2]++;

                break;

            case 'D':
                alphabet[3]++;

                break;
            
            case 'E':
                alphabet[4]++;

                break;
            
            case 'F':
                alphabet[5]++;

                break;
            
            case 'G':
                alphabet[6]++;

                break;
            
            case 'H':
                alphabet[7]++;

                break;
            
            case 'I':
                alphabet[8]++;

                break;
            
            case 'J':
                alphabet[9]++;

                break;
            
            case 'K':
                alphabet[10]++;

                break;
            
            case 'L':
                alphabet[11]++;

                break;
            
            case 'M':
                alphabet[12]++;

                break;
            
            case 'N':
                alphabet[13]++;

                break;
            
            case 'O':
                alphabet[14]++;

                break;
            
            case 'P':
                alphabet[15]++;

                break;
            
            case 'R':
                alphabet[16]++;

                break;
            
            case 'S':
                alphabet[17]++;

                break;
            
            case 'T':
                alphabet[18]++;

                break;
            
            case 'U':
                alphabet[19]++;

                break;
            
            case 'W':
                alphabet[20]++;

                break;
            
            case 'V':
                alphabet[21]++;

                break;
            
            case 'X':
                alphabet[22]++;

                break;
            
            case 'Y':
                alphabet[23]++;

                break;
            
            case 'Z':
                alphabet[24]++;

                break;
            
            default:
                    break;
            }
        }
    }
    catch (ios::failure const & exception) {
        cout << "Nie udalo sie przeczytac z pliku poniewaz: " << exception.what() << ".";
    }

    // Sprawdz jaka wartość jest najwyższa.

    unsigned int highest;

    highest = alphabet[0];

    for (int iterator = 0; iterator < 25; iterator++) {
        if (alphabet[iterator] > highest)
            highest = alphabet[iterator];
    }

    // Rysowanie histogramu.

    for (int iterator0 = highest; iterator0 >= 1; iterator0--) {
        // Szukamy które litery mają aktualną wartość zmiennej "i".

        for (int iterator1 = 0; iterator1 < 25; iterator1++) {
            if (alphabet[iterator1] >= iterator0)
                cout << "*";
            else
                cout << " ";
        }

        cout << endl;
    }

    cout << "ABCDEFGHIJKLMNOPRSTUWVXYZ" << endl;
}