Проблемы, связанные с указателями



Самые распространенные проблемами при использовании указателей:

1) попытка работать с указате­лем, не содержащим адреса ОП, выделенной под переменную;

2) потеря значения указателя из-за присваивания ему нового
значения до освобождения ОП, которую он адресует;

3) неосвобождение ОП, запрошенной с помощью функции выделения динамической памяти.

При объявлении указателя на значение любого типа ОП для ад­ресуемого значения не резервируется. Выделяется только ОП для переменной-указателя, но указатель при этом не получает никакого значения. Рассмотрим пример, содержащий грубую ошибку – попытку работать с не­инициализированным указателем:

int *х; //указателю х выделена ОП, но х не содержит адреса ОП для

      //конкретной переменной

*x=123; //ошибка

Присваивание ошибочно, так как указатель х не получил значения адреса, по которому должно быть расположено значение 123.

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

Исправить ситуацию можно с помощью одной из функций выделения динамической памяти, например функции malloc(). Форма обращения к функции malloc():

<имя_указателя> = (<тип_указателя>)malloc(<объем_ОП>);

где <имя_указателя> – имя переменной-указателя; <тип_указателя> – тип значения, возвращаемого функцией malloc(); <объем_ОП> – количество байтов ОП, выделяемой адресуемой переменной.

Например,

х=(int*)malloc(sizeof(int));

При этом из кучи выделяется 4 байта ОП для целого значения, а адрес его размещения заносится в указатель х. При таком выделении памяти работоспособность программы не нарушается. Аргумент функции malloc() определяет объем ОП для целого значения с помощью операции определения объема ОП для размещения значения целого типа sizeof(int). Операция (int*), записанная перед функцией, означает, что адрес, возвращаемый функцией malloc(), рассматривается как указатель на переменную целого типа. Это операция приведения типов.

Таким образом, ошибки не будет при использовании операторов:

int *х; //x – имя указателя

x=(int*)malloc(sizeof(int));

//выделена ОП целому значению, на которое указывает х, и переменой х

//присваивается адрес выделенной ОП

*x=123; // переменная, на которую указывает х, получила значение

         // 123

Освобождение ОП в куче выполняет функция free(). Ее аргументом является имя указателя, содержащего адрес освобождаемой и ранее выделенной динамической памяти, например free(x);

Поразрядные операции

Каждая переменная, объявленная в программе занимает в ОП некоторый участок, который содержит столько байт, сколько требует для размещения в ОП тип переменной. В свою очередь каждый байт – совокупность из восьми бит. Бит – это минимальная единица хранения информации в памяти компьютера. В языке С++ изменять значение переменной можно не только при помощи операции присваивания (т.е. целиком), но и с помощью поразрядных операций (по битам). Поразрядные операции называют побитовыми операциями – операции, с помощью которых можно изменять значение переменной по битам. Побитовыми операциями являются проверка (логические), сдвиг и присвоение значения битам данных. Такие операции могут осуществляться только над переменными типа int или char. Побитовые операции и результаты их работы приведены в табл.7.

Таблица 7

 Значение операнда 1 (оп1)

Значение операнда 2 (оп2)

Название операции

Поразрядное отрицание Исключающее ИЛИ Поразрядное И Поразрядное ИЛИ
~(оп1) (оп1)^(оп2) (оп1)&(оп2) (оп1)|(оп2)

Результат

0 1 1 1 0 1
0 0 1 0 0 0
1 1 0 0 1 1
1 0 0 1 0 1

При помощи операций легко проверить «состояние» того или иного бита переменной. Для этого на переменную «накладывают» маску – переменную, которая содержит проверяемую последовательность 0 и 1. Типы маски и переменной, на которую накладывается маска, должны совпадать. Наложение осуществляется при помощи операций ^, &, | :

Результат двойного побитового отрицания – первоначальное значение переменной
~11001101 == 00110010

~00110010 == 11001101

^

11001101

&

11001101

|

11001101
01101100 01101100 01101100
  10100001   01001100   10101101

 

Кроме поразрядных операций (см. табл.7) существуют две побитовые операции сдвига: << – операция сдвига всех битов переменной влево; >> – операция сдвига всех битов переменной вправо. При использовании этих операций указывается число разрядов, на которое происходит сдвиг битов вправо или влево. Побитовый сдвиг не является циклическим, т.е. если при сдвиге битов произошел выход за границы участка ОП, где размещена изменяемая переменная, то вышедшие за границу биты теряются. С помощью операций сдвига можно умножать или делить переменную на число, кратное двум. Форма записи этих операций:

<имя_переменной> >> <количество_разрядов>;

<имя_переменной> << <количество_разрядов>;

Например,

p=p>>3; //значение переменной p уменьшается в 23 = 8 раз

c=c<<4; // значение переменной p увеличивается в 24 = 16 раз

Наиболее часто побитовые операции используются при системном программировании. Однако они бывают полезны и при решении прикладных задач.

 

Лекция 13

Динамические массивы

Цели:

ü получить представление о способах объявления и обращения к элементам массивов;

ü освоить методику написания алгоритмов с использованием динамических массивов, перевода таких алгоритмов на язык программирования С++ и разработки соответствующего проекта в среде Visual C++ 6.0.

 


Дата добавления: 2019-11-25; просмотров: 186; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!