Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Шпоры к экзамену по программированию в 1 семест....doc
Скачиваний:
26
Добавлен:
22.04.2019
Размер:
576 Кб
Скачать

26. Проблемы и типичные ошибки при работе с указателями.

Неинициализированные указатели. Указатели являются переменными, и как любые другие переменные они должны быть инициализированы перед использованием, либо присваиванием им другого указателя, либо используя подпрограммы типа New или GetMem, ; P1 := @I; // OK: используя оператор @ P2 := P1; // OK: присвоением другого указателя New(P3); // OK: New Dispose(P3);end;Если вы просто объявите, скажем, указатель типа PInteger, но не проинициализируете его, то указатель, вероятно, будет содержать какие-то случайные байты, т.е. он фактически будет указывать на случайное место в памяти.Если вы попробуете получить доступ к памяти по такому указателю, будут происходить плохие вещи. Если эта память не будет зарезервирована вашим приложением, то вы получите исключение Access Violation - вылет программы (program crash). Но если эта память будет частью вашей программы, то вы можете переписать данные, которые не должны меняться. Если эти данные используются в другой части вашей программы, то чуть позже, получаемые результаты выполнения вашей программы будут ошибочны. Такие ошибки чрезвычайно тяжело искать.

.Мусорные указатели Мусорные указатели (stale pointers) - это указатели, которые когда-то были допустимыми, но теперь уже нет. Это может произойти, если память, на которую указывает указатель, была освобождена и/или повторно использована.

Один из частых случаев мусорного указателя - это когда память освобождается, но сам указатель ещё используется после этого. Для предотвращения этого некоторые программисты всегда устанавливают указатели в nil, после освобождения памяти. Они также проверяют на nil, прежде чем пытаться получить доступ к памяти. Другая частая ошибка: иметь более чем один указатель на один и тот же кусок памяти. Вы можете освободить память по одному указателю и даже об-nil-ить его, но другой указатель всё также будет содержать старое значение, указывающее на уже освобождённую памятьНапример, частой грубой ошибкой является функция, возвращающая указатель на свои локальные переменные. Ведь как только мы выходим из подпрограммы, её локальные данные больше не существуют, а возвращённый вызывающей стороне указатель оказывается указывающим в никуда Использование неверного базового типа.Тот факт, что указатели могут указывать на любое место в памяти и что два указателя разных типов могут указывать на одно и то же место, фактически означает, что вы можете обращаться к одной памяти разными способами. Используя указатель на Byte (^Byte), вы можете изменять индивидуальные байты Integer-а или любого другого типа.Но вы также можете что-то ошибочно записать или прочитать. Например, если вы получаете доступ к месту, которое хранит только Byte, с помощью указателя на Integer, вы можете записать 4 байта, среди которых только 1 байт является допустимым, а остальные три - просто Указатели также позволяют вам установить значение переменной без присваивания его самой переменной. Это может сильно огорчать вас во время отладки..Владельцы и забытая память (Owners and orphans)Указатели не только могут иметь различные базовые типы, но и различную семантику владения. Если вы выделяете память, Одно из общих правил: тот, кто выделяет память, обязан её и освободить, так что это ваша обязанность. Частая ошибка: использовать указатель для выделения памяти, а затем снова использовать этот же указатель для выделения другой памяти или присвоить ему другой указатель. Указатель, который содержал адрес старого выделенного блока памяти, начинает указывать на новый блок памяти, а старый адрес оказывается потерян навсегда Другими словами, у нас есть утечка памяти. Заметим, что указатель не обязан владеть памятью. Указатели часто используются для движения по массиву или для получения доступа к части структуры. Если вы не выделяете для указателя память, то нет никакой причины не изменять его. При этом указатель используется как временная переменная, которую можно в любой момент выбросить, не заботясь об освобождении памяти.

27. Константные указатели и указатели на константу.

Константные указатели - это указатели, значение которых не изменяется в ходе выполнения программы. Они описываются так:указательный_тип const имя инициализатор

Пример: int a; int * const p=&a; Изменить значение константного указателя нельзя, а изменить значение памяти, адрес которой находится в указателе – можно:

p++; // нельзя!(*p)++; // можно!]Указатели на константу - Это указатели, содержащие адрес константы.Они описываются так:const указательный_тип имя [инициализатор]

Пример: int a; const int *p=&a; Изменить значение такого указателя можно, а изменить значение памяти, адрес которой находится в указателе – нельзя: p++; // можно!

(*p)++; // нельзя! a++; // можно!

28. Массивы. Статические массивы. Реализация одномерных массивов с помощью указателей. Массив – это структурированный тип данных, состоящий из элементов (компонентов) одного и того же типа, называемого базовым.

Статические массивы Количество элементов в статическом массиве известно при написании программы и никогда не меняется. Память под такой массив выделяет компилятор.

Организация массивов с использованием указателей int Vector [10];

Реализация этого описания выполнена так: Vector представляет собой константный указатель, содержащий адрес первого из элементов массива. Память под элементы выделяется подряд: *Vector– обращение к начальному элементу(с индексом 0)

*(Vector+1) – обращение к элементу с индексом 1

*(Vector+i) – обращение к элементу с индексом i Таким образом, запись Vector[i] – синоним записи *(Vector+i) Кроме того Vector – это тоже самое, что &Vector[0]

29. Обработка одномерных массивов (заполнение массива, поиск в массиве, преобразование элементов, сортировка).// array.cpp: определяет точку входа для консольного приложения.#include "stdafx.h" #include <iostream>using namespace std;

int main(int argc, char* argv[]){ cout << "obrabotka massiva" << endl; int array1[16] = { 5, -12, -12, 9, 10, 0, -9, -12, -1, 23, 65, 64, 11, 43, 39, -15 }; // объявление и инициализация одномерного массива cout << "indeks" << "\t\t" << "element massiva" << endl; // печать заголовков for (int counter = 0; counter < 16; counter++) //начало цикла { //вывод на экран индекса ячейки массива, а затем содержимого этой ячейки, в нашем случае - это целое число cout << "array1[" << counter << "]" << "\t\t" << array1[counter] << endl; } system("pause"); return 0;}2. // array_sum.cpp: определяет точку входа для консольного приложения. #include "stdafx.h"#include <iostream>using namespace std; int main(int argc, char* argv[]){ int array1[10]; // объявляем целочисленный массив cout << "Enter elementi massiva: " << endl; int sum = 0; for ( int counter = 0; counter < 10; counter++ ) // цикл для считывания чисел cin >> array1[counter]; // считываем вводимые с клавиатуры числа cout << "array1 = {"; for ( int counter = 0; counter < 10; counter++ ) // цикл для вывода элементов массива cout << array1[counter] << " "; // выводим элементы массива на стандартное устройство вывода for ( int counter = 0; counter < 10; counter++ ) // цикл для суммирования чисел массива sum += array1[counter]; // суммируем элементы массива cout << "}\nsum = " << sum << endl; return 0;СортировкаОбычно сортировку подразделяют на два класса: внутреннюю и внешнюю. При внутренней сортировке все элементы хранятся в оперативной памяти, таким образом, как правило, это сортировка массивов. При внешней сортировке — элементы хранятся на внешнем запоминающем устройстве, это сортировка файлов. Одно из Основных требований к методам сортировки — экономное использование памяти. Это означает, что переупорядочение нужно выполнять «на том же месте», то есть методы пересылки элементов из одного массива в другой не представляют интереса. Удобной мерой эффективности является подсчет числа С — сравнений элементов и М — присваиваний элементов. Достаточно хороший алгоритм затрачивает на сортировку N элементов время порядка N×log2(N). Изначально отсортированная часть состоит из нулевого количества элементов.

procedure sort(var a : mas; n : byte);var i, j, min: byte; vsp : integer;begin for i := 1 to n - 1 do begin min:=i; for j := i+1 to n do if a[j]<a[min] then min := j; vsp:=a[i]; a[i]:=a[min]; a[min]:=vsp; end; end; Сортировка обменом (пузырьковая)Массив просматривается N-1 раз. При каждом просмотре сравниваются каждые два соседних элемента. Если элемент с меньшим индексом оказывается больше, производится их обмен.

procedure obmen(var a : mas; n : byte); var i, j: byte; vsp : integer; begin for i := 1 to n - 1 do

for j := 1 to n - i do if a[j]>a[j+1] then begin vsp:=a[j]; a[j]:=a[j+1]; a[j+1]:=vsp; end end; Сортировка вставкамиПусть часть массива отсортирована. Выбираем в отсортированной части очередной элемент и вставляем его в отсортированную так, чтобы упорядоченность элементов сохранилась. При поиске места вставки осуществляем сдвиг элементов в сторону возрастания номеров. procedure vstavka(var a : mas; n : byte);var i, j: byte; vsp : integer;begin for i := 2 to n do begin vsp:=a[i]; j:= i-1; while (a[j]>vsp) and (j>1) do beginъ

a[j+1]:=a[j]; j:=j-1 end; if (a[j]>vsp) and (j=1) then begin a[2]:=a[1];

a[1]:= vsp end else a[j+1]:=vsp; end end;

пузырек for(a=1;a<size;a++) for(b=size-1;b>=a;b- -){if(nums[b-1]>nums[b]){//меняем местами

t=numsp[b-1];nums=[b-1]=nums[b];nums[b]=t;