Добро пожаловать на Pro Pawn - Портал о PAWN-скриптинге.
Показано с 1 по 1 из 1
  1. #1
    Аватар для Tracker1
    Проверенный

    Статус
    Оффлайн
    Регистрация
    30.07.2013
    Сообщений
    54
    Репутация:
    84 ±

    [Цикл уроков программиста] 14. Указатели

    Начнём, как обычно, издалека.
    Когда операционная система запускает программу которую вы написали, все данные относящиеся к программе помещаются в оперативную память. Как вы, наверное, помните (из урока про типы данных), память в компьютере состоит из байтов. Байты в памяти нумерются, т.е. у каждого байта есть порядковый номер - его адрес. С помощью адреса процессор может обратиться к любому байту в памяти. Адреса хранятся в шестнадцатиричном формате. Обычно мы используем десятичную систему. В компьютерах удобнее всего использовать двоичную (числа формируются всего из двух цифр: 0 и 1), или степень двойки (16 = 24). При этом в шестнадцатиричной системе 16 цифр: 0, 1, ... 9, A, B, C, D, E, F (где цифры от a до f соответствуют десятичным 10-15). Адрес выглядит примерно так: 0x0012fc2c, что равно числу 1244204 в десятичной системе счисления. 0x (ноль икс) перед адресом говорит, что число шестнадцатиричное.

    Ну так вот, у каждого байта есть свой адрес. Когда ваша программа попадает в оперативную память, всем данным присваиваются адреса, чтобы процессор мог к ним обращаться. Каким данным присваиваются адреса: переменным, функциям, структурным переменным, объектам классов.

    При этом данные хранятся в двух разных участках памяти: стеке и куче:
    PHP код:
    int a 0;

    int main()
    {
      
    int b 0;
      return 
    0;


    a хранится в куче (heap), b хранится в стеке (stack). Все глобальные переменные попадают в кучу, все локальные переменные попадают в стек. что такое стек, мы рассмотрели в прошлом уроке. Куча же - это область памяти где данные никак не организованы.

    Теперь, рассмотрим простое объявление переменной:
    PHP код:
    int a

    a - идентификатор с помощью которого мы можем обращаться к какой-то области памяти. При этом данная переменная занимает четыре байта. Обращение к переменной происходит по первому байту.

    При создании переменной a, ей присваивается адрес 0x0012fc28 (адрес взят для примера). Процессор обращается к этой переменной с помощью этого адреса. Сама же переменная занимает в памяти следующие байты: 0x0012fc28, 0x0012fc29, 0x0012fc2a, 0x0012fc2b (обратите внимание на последние цифры в адресах). Где первый байт - адрес переменной.

    Теперь мы плавно переходим к рассмотрению указателей.

    Следует заметить, что указатели есть не во всех языках программирования.

    Указатель (pointer) - это переменная, значением которой является адрес.

    Чтобы получить адрес обычной переменной можно воспользоваться следующим синтаксисом:

    PHP код:
    cout << &a

    На экран будет выведен адрес переменной a.

    Указатель можно создать следующим образом:
    PHP код:
    int a 0;
    intptr;

    ptr = &a;

    cout << ptr
    Здесь & - операция получения адреса. Не путайте использование & для получения адреса и для передачи в функцию значения по ссылке.

    В данном примере мы создали указатель ptr на тип int и присвоили этому указателю адрес, где хранится переменная a.

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

    Через указатель можно изменять значение хранящееся по адресу. Для этого указатель нужно разыменовать:
    PHP код:
    int a 0;
    intptr = &a;

    *
    ptr 10// то же самое что и: a = 10;

    cout << aвывод на экран10 
    Здесь мы воспользовались указателем, чтобы изменить значение переменной a. В данном случае * - операция разыменования. Не путайте с объявлением указателя!

    При разыменовании указателя мы получаем доступ к значению адреса в памяти, на который указывает указатель.
    Указатель на тип void

    До сих пор мы использовли ключевое слово void (void - недействительный, пустой) в заголовках функций, когда нам не нужно было возвращать никаких значений.

    Указатели на разные типы не могут использоваться друг с другом:
    PHP код:
    int a 0;
    float b 1;

    intptr_a = &a;
    floatptr_b = &b;
    // ptr_a = ptr_b; // Так нельзя!!! 
    Чтобы обойти это ограничение можно воспользоваться указателем на void:
    PHP код:
    int a 0;
    float b 1;
    char c 2;

    intptr_a = &a;   // Указатель на тип int
    floatptr_b = &b// Указатель на тип float
    voidptr_c = &c;  // указатель на void

    // ptr_a = ptr_b; // Так нельзя!!!

    ptr_c ptr_a// Оба варианта
    ptr_c ptr_b// корректны 

    Указатели на void особенно полезны при использовании с классами. В DirectX очень многие объекты создаются как указатели на void.
    Указатели-константы

    Мы уже не раз встречались с указателями-константами. Это такие указатели, значение которых не может быть изменено. Т.е. не могут быть изменены адреса. Значения же хранящиеся в адресах могут изменяться.

    Более известное имя указателей-констант - массивы.

    Хотя надо заметить, что данный вид указателей используется не только с массивами:
    PHP код:
    int a 1;
    int b 5;
    intptr_a = &a;
    int* const ptr_b = &b;
    *
    ptr_b 2;
    // ptr_b = ptr_a; // так не получится. ptr_b - константа
    *ptr_b 3
    Передача аргумента в функцию по указателю

    Мы уже умеем передавать аргументы в функции двумя способами: по значению и по ссылке. Третий способ - передача по указателю (pass-by-pointer).
    PHP код:
    int main()
    {
      
    int a 10;
      
    pass_by_pointer(&a); // Передача адреса переменной
      
    cout << a// 5
      
    return 0;
    }

    void pass_by_pointer (voidptr)
    {
    *
    ptr 5;


    При передаче по указателю мы передаём адрес. Внутри функции параметр ptr может непосредственно влиять на содержимое внешней переменной a.
    Операция new

    В программе pseudo_game мы использовали заданный массив. Мы не могли сделать что-нибудь подобное:
    PHP код:
    cin >> s// количество строк
    cin >> c// количество столбцов

    char map[s][c]; 
    Компилятор должен знать заранее (до начала выполнения программы) сколько памяти выделить на массив. То же самое и с классами: все объекты классов должны быть созданы до начала выполнения программы. Вы не можете динамически создать объект.

    Операция new позволяет обойти это ограничение. (В примере используется класс tank из программы pseudo_game):
    PHP код:
    /*
    Здесь представлены сразу два примера:
    создание указателя на tank
    и
    выделение памяти для хранения всех клеток игрового поля
    */

    tankt34 = new tank// выделение памяти под объект tank
                          // Здесь t34 - указатель на тип tank

    int s,c// количество строк и столбцов
    cin >> s;
    cin >> c;


    char** map// указатель
    map = new char*[s];// указателю присваем адресс массива указателей char*
    for(int i=0;i<s;i++) map[i]=new char[c]; 
    Здесь t34 и map - указатели.

    В данном примере мы вынуждены использовать одномерный массив для представления двумерного. Как в данном случае получить доступ к произовльному элементу? Вот как выглядит инициализация клетки (5,6):

    PHP код:
    map[5][6]='T'
    Думаю, данный пример нуждается в пояснениях. Мы создаем указатель на массив указателей типа char.
    Потом мы должны каждому указателю присвоить массив char. Это очень тяжело на первый взгляд. Попробуй-те запустить этот пример и затем отследить его отладчиком
    Операция delete

    Операция delete используется для освобождения памяти после того как она стала не нужна, дабы не допустить утечки памяти:
    PHP код:
    for(int i=0;i<s;i++) delete [] map[i];
    delete [] map;

    delete t34
    Доступ к объектам

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

    t34->fuel = 100;

    То есть вместо точки используются символы ->.

    Прошу заметить, что динамический двумерный массив я написал сам, а не автор урока.
    Причина - сильно устарелый метод. Для того чтобы вам лучше понять указатели, я тоже написал на указателях.
    Есть более современный и простой метод для написания динамических массивов, но он требует библиотеку std и будет работать лишь в С++.
    PHP код:
    #include <vector>
    using namespace std;
    int main()
    {
    vector<intx//vector - объект. <int> - тип данных хранимых в этом объекте. x - название объекта
    x.push_back(5); // занести в массив x пятерку. Теперь x[0] = 5.
    vector<int> array(4,100); // массив с четырьмя элементами равных 100. 
    array.resize(10); //расширить массив до 10 ячеек. Теперь массив выглядит так 100,100,100,100,0

    На сегодня всё.
    Последний раз редактировалось bredvix; 25.02.2014 в 18:00.

 

 

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •