Суббота, 11.05.2024, 06:11
Discovery
Главная Регистрация Вход
Приветствую Вас, Гость · RSS
Меню сайта
Форма входа
Категории раздела
PHP [1]
Уроки по PHP для начинающих
C++ [18]
Статьи посвященные С++
XML [2]
Статьи по XML
Математика программистам [0]
Нужна ли программистам математика? Да вроде и не нужна... Или все же нужна? И если да то в каком объеме? Давайте попробуем разобраться в этом вместе!
Поиск
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
     Каталог статей
    -->
    Главная » Статьи » Программирование » C++

    Как правильно использовать указатели.

    Как правильно использовать указатели.

    Кое-кто может подумать: «Зачем себе усложнять жизнь указателями, если можно прекрасно обойтись и без них?» Если есть циклы, то зачем их дублировать с помощью указателей? Действительно зачем?

    Прежде чем ответить на этот вопрос, я хочу сделать одно замечание: «Все программы, которые мы пишем, являются учебными». Обычно учебные программы используют малый объем памяти и поэтому очень не требовательные к компьютерным ресурсам (а именно к оперативной памяти). Что не скажешь о профессиональных программах. Оперативная память не бесконечна. Если забыть об этом, то можно написать программу, которая будет постоянно «тормозить» или еще хуже, приводить к «зависанию» компьютера. А этого не кто не любит. Поэтому если вы собираетесь всю жизнь писать небольшие программы и запускать их в окне консоли, то тогда вам вряд ли они понадобятся. Если же вы не собираетесь на этом останавливаться, тогда вы должны уметь работать с указателями. Вот почему многие профессиональные программисты будут возражать, если вы скажете, чтобы из языка С++ нужно удалить указатели, и расскажут множество примеров когда очень трудно обойтись без них.

    И так теперь мы знаем, что в профессиональном программировании без указателей обойтись очень трудно (разумеется когда идет речь об языке С++, в языке JAVA указателей нет).

    Теперь давайте на минутку отвлечемся от указателей и представим себе человека, который что-то делает ручным инструментом для себя. Почему бы и нет. Но это если для себя. Когда речь идет о производстве чего-либо в промышленных масштабах, то такое производство не мыслимо без применения станков. А так как станки являются не безопасными, то вас не допустят к работе на них, пока вы не пройдете инструктаж по технике безопасности. Поэтому эту статью я хотел бы назвать своеобразной инструкций по безопасному использованию указателей для начинающих программистов.

    И так первое о чем я хотел бы поговорить, это о корректном сравнении указателей. Для начала я приведу пример, а затем объясню, почему так делать нельзя. И так пример программы, в которой выполняется некорректное сравнение указателей.

    /* Эта программа является
        примером некорректного
        сравнения указателей
    */

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char test1, test2;
        char *p1, *p2;

        p1 = &test1;
        p2 = &test2;

        cout << (p1 - p2);

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    На первый взгляд здесь все в порядке. Но никто не гарантирует, что если вы запустите эту же программу на компьютере, на котором установлена другая операционная система или другие программы, результаты будут такими же. Эти две переменные между собой никак не связаны, и поэтому могут располагаться в любом месте оперативной памяти. В отличие от массивов, значения которых всегда следуют друг за другом независимо от операционной системы и запущенных на данный момент программ. Тоже самое, когда речь идет о двух массивах. Никто не даст вам гарантии, что они находятся рядом.

    В предыдущем уроке я приводил пример использования указателей для вывода всех элементов массива на экран. Теперь давайте представим, что вам потребовалось еще раз вывести содержимое того же массива с помощью указателя, который вы использовали для вывода массива. Если не переустановить его на первый элемент массива, можно получить логическую ошибку, которую трудно будет обнаружить. Программа скомпилируется и даже можно будет запустить исполняемый файл, но результат выполнения программы будет совсем не тот, который вы ожидаете. Вот как будет выглядеть программа, в которой забыли переустановить указатель:

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        int *pointer;
        int number[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        pointer = number;

        for(int i=0; i<10; i++)
        {
            cout << *pointer << " ";
            pointer++;
        }

        cout << "\n\n";

        for(int i=0; i<10; i++)
        {
            cout << *pointer << " ";
            pointer++;
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    А вот как выглядит результат работы этой программы на моем компьютере:

    1 2 3 4 5 6 7 8 9 10

    4072360 8 2009247405 23329828 2088810195 2293612 37 2 2293680 4198887

    Для продолжения нажмите любую клавишу . . .

    Вас, наверное, удивляет, откуда такие большие числа? Все объясняется довольно просто. Дело в том, что после того, как мы вывели содержимое массива на экран, наш указатель содержал адрес десятого элемента нашего массива. После того, как мы решили повторно вывести содержимое нашего массива, указатель вышел за пределы массива, и программа попробовала интерпретировать содержимое ячеек, как значения типа int. Давайте, исправим нашу программу.

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        int *pointer;
        int number[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        pointer = number;

        for(int i=0; i<10; i++)
        {
            cout << *pointer << " ";
            pointer++;
        }

        cout << "\n\n";

        pointer = number;

        for(int i=0; i<10; i++)
        {
            cout << *pointer << " ";
            pointer++;
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    Теперь программа работает корректно. И, наконец, ошибка, о которой больше всего спорят. Неинициализированный указатель. Давайте разберемся, что же это такое, и как ее избежать. Дело в том, что если попытаться записать значение, используя указатель, которому не присвоен адрес переменной, может произойти перезапись системных данных, что повредит вашу операционную систему или даже разрушить ее. Причем чем больше данных вы пытаетесь изменить при помощи неинициализированный указателя, тем больше вероятность этого. Так что же, теперь вообще не использовать указатели?

    Давайте я попытаюсь объяснить это на примере автомобиля. Наверное, каждый, кто хоть раз видел автомобиль, умеет на глаз определить успеет он перейти дорогу или нет. Для этого ему достаточно взглянуть на едущий по ней автомобиль. То же самое и с указателями. Пока мы пишем сами всю программу от начала до конца, мы всегда сможем узнать инициализировали мы указатель или нет. Да же если в ней больше тысячи строк кода.

    Совсем другое дело, когда пьяный водитель выезжает на тротуар. Никто из пешеходов этого просто не ожидает. В профессиональном программировании программа обычно разбивается на несколько файлов, что позволяет работать над программой нескольким человекам одновременно. Но, к сожалению, это иногда приводит к тому, что нельзя просмотреть всю программу от начала до конца.

    На дорогах с большим движением обычно ставят специальные барьеры, которые призваны защитить пешеходов от случайного въезда машины на тротуар. Есть такие барьеры в С++ от неинициализированного указателя. При объявлении указателя его можно инициализировать значение NULL. Выглядит это так:

    char *test = NULL;

    Что это нам дает?  В оперативной памяти есть особая область, которая называется NULL. Она не может содержать какие-либо данные. Если при объявлении указателя мы инициализировали его значением NULL, то когда мы попытаемся с помощью его изменить данные, они запишутся в область NULL. Можно записать то же самое, присвоив указателю значение 0. Вот так:

    char *test = 0;

    Это то же самое, что присвоить указателю значение NULL. Теперь мы можем с помощью конструкции if проверить инициализирован наш указатель или нет:

    #include <iostream>
    #include <windows.h>
    using namespace std;

    int main()
    {
        char *p = 0;
        char *p1;

        if(p == NULL)
        {
            cout << "Указатель p инициализирован \n";
        }
        else
        {
        cout << "Указатель p не инициализирован \n";
        }

        if(p1 == NULL)
        {
            cout << "Указатель p1 инициализирован \n";
        }
        else
        {
            cout << "Указатель p1 не инициализирован \n";
        }

        cout << "\n\n";
        system("PAUSE");
        return 0;
    }

    На этом мы заканчиваем изучение указателей. В следующем уроке мы начнем изучать функции.

    Категория: C++ | Добавил: stranger140569 (19.05.2013)
    Просмотров: 1288 | Рейтинг: 5.0/1
    Всего комментариев: 0
    Имя *:
    Email *:
    Код *:
    Copyright MyCorp © 2024
    Конструктор сайтов - uCoz