Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Электонный конспект.doc
Скачиваний:
3
Добавлен:
13.11.2019
Размер:
746.5 Кб
Скачать

Оператор “выражение”

Любое выражение, завершающееся точкой с запятой, рассматривается как оператор, выполнение которого заключается в вычислении выражения. Частным случаем выражения является пустой оператор ; (он используется, когда по синтаксису оператор требуется; а по смыслу — нет). Примеры:

i++; // выполняется операция инкремента

а* = b + с; // выполняется умножение с присваиванием

fund(k); // выполняется вызов функции

Н а схеме алгоритма

Операторы выбора

Оператор if

Стандартная форма оператора if следующая:

if (выражение) оператор;

[else оператор;]

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

Если выражение истинно (любое значение, кроме 0), выполняется блок операторов, следующий за if; иначе выполняется блок операторов, следующий за else. Всегда выполняется код, ассоциированный или с if, или с else, но никогда не выполняются оба кода одновременно.

На схеме алгоритма

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

-2

Запишем условие попадания в виде неравенств

х2+у2<=1 или{х<=0 и у<=0 и х+у>=-2

#include <stdio.h>

int main(void)

{

float x,y;

printf(“Введите ваше числa: “);

scanf(“%f%f”, &x,&y);

if(x*x+y*y<=1 || x<=0 && y<=0 && x+y>=-2) printf(“Попадает”);

else printf(“Не попадает”);

return 0;

}

ВНИМАНИЕ!

Распространенная ошибка при записи условных операторовиспользование в выражениях вместо проверки на равенство (==) простого присваивания (=), например,

if(a=-l)b=0;

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

Вторая ошибка — неверная запись проверки на принадлежность диапазону. Например, чтобы проверить условие 0<х<1. нельзя записать его в условном операторе непосредственно, так как будет выполнено сначала сравнение 0<х, а его результат (true или false, преобразованное в int) будет сравниваться с 1. Правильный способ записи: if(0<x && х<1)„

Вложенные if. Под вложенным if подразумевается оператор if, имеющий еще один if или else., проблематичность вложенности операторов if заключается в трудностях идентификации: какое else соответствует какому if. Например:

if (x)

if (y) printf(“1”);

else printf(“2”);

Какому if соответствует какое else?

Для разрешения такого рода проблем существует правило: В С else соответствует ближайшему предшествующему if (на том же уровне видимости), еще не имеющему оператора else. В данном случае else связан с оператором if(y). Для того, чтобы связать else с оператором if(x), следует использовать фигурные скобки, как показано ниже:

if (x) {

if (y) printf(“1”);

}

else printf(“2”);

Лесенка if-else-if. Типичной программистской конструкцией является лесенка if-else-if. Она выглядит следующим образом:

if(выражение)

оператор;

else if(выражение)

оператор;

else if(выражение)

оператор;

.

.

.

[else

оператор;]

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

#include <stdio.h>

/* программа «угадай число 4» */

int main(void)

{

int magic = 123; /* искомое число */

int guess;

printf(“Введите ваше число: “);

scanf(“%d”, &guess);

if(guess == magic) {

printf(“Правильно”);

printf(“%d есть угаданное число”,magic);

}

else if(guess > magic) {

printf(“Неправильно. Больше”);

else printf(“Неправильно. Меньше”);

return 0;

}

Оператор ?. Оператор ? может использоваться для замены стандартной конструкции if/else:

Ограничением в данном случае является использование единственного выражения как после if, так и после else.

Оператор ? называется триадным (тернарным) оператором, поскольку ему требуется три операнда и имеет следующий вид:

выражение_1 ? выражение_2 : выражение_3

где выражение_1, выражение_2 и выражение_3 – это выражения.

Оператор ? работает следующим образом. Вычисляется выражение_1. Если оно истинно, вычисляется выражение_2 и вся конструкция получает вычисленное выражение. Если выражение_1 ложно, вычисляется выражение_3 и вся конструкция получает вычисленное выражение. Например:

x = 10;

y = x>9 ? 100 : 200;

В данном примере у получает значение 100. Если бы х было меньше 9, то у получило бы значение 200. Ниже приведен фрагмент программы, выполняющий такие же действия, но с использованием операторов if/else:

x = 10;

if(x>9) y = 100;

else y = 200;

Оператор принятия решений switch

Хотя конструкция if-else-if может выполнять многочисленные проверки, она не очень элегантна. Код очень труден для восприятия. С этой целью язык С++ имеет оператор принятия решений switch, выполняющий действия, основываясь на сравнении значения со списком констант символов или целых чисел. При обнаружении выполняется оператор или операторы, ассоциированные с данным значением. Оператор switch имеет следующий вид:

switch (выражение) {

case константа_1 :

последовательность_операторов

break;

case константа_2 :

последовательность_операторов

break;

case константа_3 :

последовательность_операторов

break;

.

.

[default :

последовательность_операторов]

}

Оператор default выполняется, если не найдено соответствий. default необязателен и, если его нет, то в случае отсутствия совпадений ничего не происходит. Когда обнаруживается совпадение, операторы, ассоциированные с соответствующим case, выполняются до тех пор, пока не встретится оператор break. В случае default (или последнего case, если отсутствует default), оператор switch заканчивает работу при обнаружении конца.

Следует знать о трех важных моментах оператора switch:

1) switch отличается от if тем, что он может выполнять операции проверки строгого равенства, в то время как if может вычислять логические выражения и отношения.

2) Не может быть двух констант в одном операторе switch, имеющих одинаковые значения, но константы должны иметь один тип. Конечно, оператор switch, включающий в себя другой оператор switch, может содержать аналогичные константы.

3) Если в операторе switch используются символьные константы, они автоматически преобразуются к символьным значениям.

4 ) если выход break из switch явно не указан, последовательно выполняются все остальные ветви.

Пример

#include <stdio.h>

#include <conio.h>

void main(void)

{

int a,b,z;

char znak;

printf(“Введите значение а операцию(+,-,*,/,%)и b, \n”);

scanf("%d%c%d",&a,&znak,&b);

switch(znak) {

case ‘+’:z=a+b;break;

case ‘-’:z=a-b;break;

case ‘*’:z=a*b;break;

case ‘/’:z=a/b;break;

case ‘%’:z=a%b;break;

default :printf(“НеДопустимая операция”);

}

if ((znak=='+')||(znak=='-')||(znak=='*')||(znak=='/')||(znak=='%'))

printf("a %c b = %d",znak,z);

}

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

#include <stdio.h>

#include <conio.h>

void main(void)

{

int key;

printf(“Нажмите одну из курсорных клавиш\n”);

key=getch();key=getch();

switch(key) {

case 77: case 75:case 72:case 80: printf(“Strelki”);break;

default :printf(“ Ne strelki”);

}

}

Вложенные операторы switch. Один оператор switch может иметь среди последовательности операторов другой оператор switch. Даже если константы case внутреннего и внешнего операторов имеют одинаковые значения, не возникнет никакого конфликта. Следующий фрагмент программы совершенно корректен:

switch (x) {

case 1:

switch (y) {

case 0: printf(“Ошибка. Деление на ноль.”);

break;

case 1: process(x, y);

}

break;

case 2:

.

Операторы цикла

В языке С и в других современных языках программирования операторы цикла (итераций) позволяют выполнять набор инструкций до тех пор, пока не выполнится некоторое условие.

Оператор цикла for

Стандартный вид цикла for следующий:

for(инициализация; условие; Модификации) тело цикла;

Оператор for имеет три главные части:

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

2) Условие – это место, где находится выражение, определяющее условие работы цикла.

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

Эти три важные части разделяются точкой с запятой. Цикл for работает до тех пор, пока условие истинно. Когда условие становится ложным, выполнение программы продолжается с оператора, следующего за циклом for.

Н а схеме алгоритма

Пример (программа печатает таблицу значений функции y=х2+l во введенном диапазоне):

finclude <stdio.h>

int main()

{

float Xn, Xk, Dx. X;

printf("BBedite диапазон и шаг изменения аргумента: ");

scanf(“%f%f%f”, &Хn, &Xk, &Dx);

printf(“| X | У |\n);

for (X = Xn; X<=Xk: X += Dx)

printf('| %5.2f | %5.2f |\n", X, X*X + 1);

return 0;

}

В программе переменная x изначально установлена в Xn. Поскольку x меньше Xk, вызывается функция printf() для вывода текущего значения x,y, после чего x увеличивается на Dx и проверяется условие: по-прежнему ли x меньше либо равно Dx. Данный процесс продолжается до тех пор, пока x не станет больше Dx, и в этот момент цикл прервется. В данном примере x является переменной цикла, которая изменяется и проверяется на каждой итерации цикла.

Ниже приведен пример цикла for, повторяющего сразу несколько операторов:

for(x=100; x!=65; x=5)

{ z = sqrt(x);

printf(“Корень квадратный из %d равен %f\n);

}

Как sqrt(), так и printf(), вызываются и выполняются, пока x не равно 65. Следует обратить внимание, что в цикле переменная x уменьшается: сначала она получает значение 100 и на каждой итерации цикла происходит уменьшение на 5.

Оператор цикла while

Стандартный вид оператора while следующий:

while(условие) тело_цикла;

где оператор – это или пустой, или простой, или составной оператор. Условием может быть любое выражение, имеющее в качестве истины ненулевое значение. Цикл выполняется, пока условие истинно. Когда условие становится ложным, выполняется инструкция, следующая за циклом. Если условие при первой проверке ложно, тело_цикла не выполняется ни разу.

Н а схеме алгоритма оператор while отображается следующим образом:

Следующий пример показывает процедуру, обрабатывающую ввод с клавиатуры, работающую, пока не будет введен символ А:

char ch;

while(ch != ‘A’) ch = getche();

Сначала ch устанавливается в 0. Цикл while начинается с проверки ch на равенство А. Поскольку ch инициализировано нулем, проверка выдаст истину и цикл начнется. При каждом нажатии клавиши на клавиатуре условие проверяется повторно. Если введено А, условие становится ложным и цикл прекращается.

Как и цикл for, цикл while сначала выполняет проверку, то есть тело цикла может вообще не выполняться

В теле цикла может и не быть никаких операторов. Например:

while((ch = getchar()) != ‘A’);

просто зацикливается до тех пор, пока с клавиатуры не введется символ А.

Пример: Написать программу вычисления значения функции cos x (гиперболический косинус) с помощью бесконечного ряда Тейлора с точностью ε по формуле:

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

В оспользуемся рекуррентной формулой для получения последующего члена ряда через предыдущий, т.е.

Сп+1=Сп*Т, где Т некоторый множитель.

Найдем его:

#include <stdio.h>

#include <conio.h>

#include <math.h>

int main(void)

{ const int MaxIter=100; //допустимое число итераций

double x,eps;

double Cn=1,y=Cn;//первый член ряда и нач. значение суммы.

int n=0; // количество итераций.

bool usl=true;

printf("Введите аргумент и точность:");

scanf("%lf%lf",&x,&eps);

while(fabs(Cn)>eps)

{Cn*=-x*x/((2*n+1)*(2*n+2));//очередной член ряда

y+=Cn;//вычисление суммы

n++;

if(n>MaxIter)

{printf("Ряд расходится.");

usl=false;

break;

}

}

if(usl)

{printf("Значение функции: %.3lf, для x=%.3lf при n=%d итераций\n",y,x,n);

printf("(Для проверки cos x=%lf)",cos(x));}

getche();

return 0;

}

Оператор цикла do/while

В противоположность циклам for и while, сначала проверяющим условие, цикл do/while проверяет условие в конце. То есть, цикл do/while всегда выполняется, по крайней мере, один раз. Стандартный вид цикла do/while следующий:

do {

последовательность_операторов;

} while (условие);

Сначала выполняется простой или составной оператор, составляющий тело цикла, а затем вычисляется выражение. Если оно истинно (не равно false), тело цикла выполняется еще раз. Цикл завершается, когда выражение станет равным false или в теле цикла будет выполнен какой-либо оператор передачи управления.

На схеме алгоритма:

П ример:

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

#include <stdio.h>

#include <conio.h>

int main(void)

{ int n,m,S,sum;

printf("");

scanf("%d",&S);

sum=0;n=0;

do{printf("");

scanf("%d",&m);

sum+=m;

n++;

}while(sum<S);

printf("n=%d",n);

getche();

return 0;

}

Наиболее типичным использованием цикла do/while является процедура выбора пунктов меню. Когда набран корректный ответ, она возвращает значение функции. Неправильный ответ приводит к повторному вводу.

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

Чтобы избежать ошибок, рекомендуется:

- проверить, всем ли переменным, встречающимся в правой части операторов присваивания в теле цикла, присвоены до этого начальные значения (а также возможно ли выполнение других операторов);

- проверить, изменяется ли в цикле хотя бы одна переменная, входящая в условие выхода из цикла;

- предусмотреть аварийный выход из цикла по достижению некоторого количества итераций;

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

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

Оператор do while обычно используют, когда цикл требуется обязательно выполнить хотя бы раз (например, если в цикле производится ввод данных).

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

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

Операторы переходов

Оператор break

Этот оператор имеет два назначения. Первое – это окончание работы оператора switch. Второе – это принудительное окончание цикла, минуя стандартную проверку условия. Когда оператор break встречается в теле цикла, цикл немедленно заканчивается и выполнение программы переходит на строку, следующую за циклом.

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

int main(void)

{

int loc;

loc = 1;

do {

loc ++;

if(kbhit()) break;

} while(loc==1000);

return ; }

Такие функции можно использовать для поиска имени в файле базы данных. Если файл слишком длинный и надоело ждать, то можно нажать на любую клавишу и выйти из функции до окончания работы. Стандартная функция kbhit() возвращает 0, если не было нажатия клавиши. В противном случае она возвращает ненулевое значение.

break вызывает выход из самого внутреннего цикла. Например:

for(t=0; t<100; t++) {

count = 1;

for(;;) {

printf(“%d “, count);

count++;

if(count == 10) break;

}

}

выводит числа от 1 до 10 включительно 100 раз. Каждый раз, когда встречается break, контроль передается внешнему циклу for.

break, используемый в операторе switch, влияет только на данный switch, но не на цикл, в котором может находится данный switch.

Оператор continue

Работа оператора continue чем-то похожа на работу оператора break. Но вместо форсирования окончания цикла continue переходит к следующей итерации цикла, пропуская оставшийся код тела цикла. Например, следующая процедура выводит только положительные числа:

do {

scanf(“%d”, &x);

if(x<0) continue;

printf(“%d “, x);

} while(x!=100);

В циклах while и do/while оператор continue вызывает переход к проверке условия и затем продолжает работу цикла. В случае цикла for выполняется часть увеличения, затем проверяется условие и, наконец, выполняется само тело цикла.

Испоьзование операторов continue и break м.представить в виде рис:

Оператор return

Оператор возврата из функции return завершает выполнение функции и передает управление в точку ее вызова. Вид оператора:

return [ выражение ];

Выражение должно иметь скалярный тип. Если тип возвращаемого функцией значения описан как void. выражение должно отсутствовать.

Ф ункция exit()

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

void exit(int статус);

Она использует заголовочный файл stdlib.h. Значение статуса возвращается в операционную систему.

Для индикации корректности завершения работы exit() традиционно вызывается с аргументом 0. Другие аргументы используются для индикации различного рода ошибок. Можно также использовать предопределенные макросы EXIT_SUCCESS (успешное завершение) и EXIT_FAILURE (завершение с ошибками) в качестве значений для статуса.

exit() используется, когда условия выполнения программы неудовлетворительны.

В примере exit() используется для выхода из программы и возврата в операционную систему:

void main(void)

{

char ch;

printf(“1. Проверка орфографии\n”);

printf(“2. Корректировка ошибок орфографии\n”);

printf(“3. Вывод ошибок на экран\n”);

printf(“4. Выход из программы\n”);

printf(“Выбери режим: “);

do {

ch = getche(); /* чтение клавиатуры */

switch(ch) {

case ‘1’: … break;

case ‘2’:… break;

case ‘3’:… break;

case ‘4’:exit(0); /* возврат в ОС */

}

} while(ch!=’1’ && ch!=’2’ && ch!=’3’);

}

Оператор goto

Хотя оператор goto уже давно не рекомендуют использовать, он по-прежнему используется в программах., т.к. бывают моменты, когда goto не только не усложняет программу, но даже ее упрощает.

Синттаксис:

goto метка;

метка: опратор;

Оператор goto требует для своей работы наличие меток. Метка – это корректный идентификатор языка С, завершаемый двоеточием. Метка должна находиться в той же функции, что и goto. Например, цикл от 1 до 100 может быть записан с использованием goto и меток следующим образом:

x = 1;

loop1:

x++;

if(x < 100) goto loop1;

Одним из наиболее целесообразных способов использования оператора goto является выход из нескольких уровней вложения. Например:

for(...) {

for(...) {

while(…) {

if(...) goto stop;

.

.

.

}

}

}

stop:

printf(“Ошибка в программе\n”);

Уничтожение goto приведет к необходимости выполнения дополнительных проверок. Простой оператор break здесь не работает, поскольку он может обеспечить выход из самого внутреннего цикла.

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

Тема 4. Язык программирования С/С++. Организация данных. Массивы и указатели.

Указатели

Когда компилятор обрабатывает оператор определения переменной, например, int i=10 он выделяет память в соответствии с типом (int) и инициализирует ее указанным значением (10). Все обращения в программе к переменной по ее имени (i) заменяются компилятором на адрес области памяти, в которой хранится значение переменной. Можно определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями.

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

Т.к. указатель – это адрес некоторого объекта, то через него м. обращаться к этому объекту.

у=&x – переменной y присваивается адрес переменной х, указывающий где в оперативной памяти находится значение х

z=*y – переменной z присваивается переменная , записанная по адресу у, если у=&х z=*у ,то z=х

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

Для указателей справедливо правило, кот. требует предварительного описания

В C++ различают три вида указателей — указатели на объект, на функцию и на void, отличающиеся свойствами и набором допустимых операций. Указатель не является самостоятельным типом, он всегда связан с каким-либо другим конкретным типом.

Указатель на объект содержит адрес области памяти, в которой хранятся данные определенного типа (основного или составного)..Простейшее объявление указателя на объект (в дальнейшем называемого просто указателем) имеет вид: тип *имя:

где тип может быть любым.

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

int *a. b, *c; описываются два указателя на целое с именами а и с, а также целая переменная b.

Указателъ на void применяется в тех случаях, когда конкретный тип объекта, адрec которого требуется хранить, не определен (например, если в одной и той же переменной в разные моменты времени требуется хранить адреса объектов различных типов). '

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

Инициализация указателей

Указатели чаще всего используют при работе с динамической памятью, называемой некоторыми эстетами кучей (перевод с английского языка слова heap). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится только через указатели. Время жизни динамических переменных — от точки создания до конца программы или до явного освобождения памяти. В С++ используется два способа работы с динамической памятью. Первый использует семейство функций mal1oc и достался в наследство от С, второй использует операции new и delete.

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

Существуют следующие способы инициализации указателя:

1. Присваивание указателю адреса существующего объекта:

• с помощью операции получения адреса;

int а = 5; II целая переменная

int* p = &a; // в указатель записывается адрес а

int* p (&a): // то же самое другим способом

• с помощью значения другого инициализированного указателя: int* г = p:

2. Присваивание указателю адреса области памяти в явном виде:

char* vp = (char *)OxB8000000:

Здесь 0xB8000000- шестнадцатеричная константа, (char *) — операция приведения типа: константа преобразуется к типу «указатель на char».

3. Присваивание пустого значения:

int* suxx = NULL; int* rulez = 0;

пустой указатель можно использовать для проверки, ссылается указатель на конкретный объект или нет.

4. Выделение участка динамической памяти и присваивание ее адреса указателю:

• с помощью операции new:

int* n = new int; // l

int* rn = new int (10); // 2

int* q = new int[10]; // 3

• с помощью функции malloc , которая находится в библиотеке malloc.h:

int*u = (int *)malloc(sizeof(int)): // 4

В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n. Память под саму переменную n (размера, достаточного для размещения указателя) выделяется на этапе компиляции.

В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10.

В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в переменную q. которая может трактоваться как имя массива.

В операторе 4 делается то же самое, что и в операторе 1, но с помощью функции выделения памяти ma11oc, унаследованной из библиотеки С. В функцию передается один параметр — количество выделяемой памяти в байтах. Конструкция (int*) используется для приведения типа указателя, возвращаемого функцией, к требуемому типу (о явном преобразовании типов см. с. 231). Если память выделить не удалось, функция возвращает 0.

Операцию new использовать предпочтительнее, чем функцию malloc, особенно при работе с объектами.

Освобождение памяти, выделенной с помощью операции new, должно выполняться с помощью delete, а памяти, выделенной функцией mal1oc — посредством функции free. При этом переменная-указатель сохраняется и может инициализироваться повторно. Приведенные выше динамические переменные уничтожаются следующим образом:

delete n; delete m; delete [] q: free (u):

Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete[]. Размерность массива при этом не указывается. Если квадратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся недоступны для дальнейших операций. Такие ячейки памяти называются мусором.

ВНИМАНИЕ

Если переменная-указатель выходит из области своего действия, отведенная под нее память освобождается. Следовательно, динамическая переменная, на которую ссылался указатель, становится недоступной. При этом память из-под самой динамической переменной не освобождается. Другой случай появления «мусора» — когда инициализированному указателю присваивается значение другого указателя. При этом старое значение бесследно теряется.

Операции с указателями

С указателями м. производить след. операции: присваивания, арифметические отношения.

Операция присваивания для указателей аналогична эт. операции для др. типов данных. В отличии от др. типов, при операции присваивания , достаточно, чтобы оба операнда были типа «указатель», но типы этих указателей м.б. различными

Напр-р, …

int a;

int *p=&a, *p1;

*p=8 /* значение 8 заносится по адресу р, т.е. значение переменой а=8*/

р1=р /*переменной р1=р, т.е адрес переменной а*/

Арифм. операции. Допустимы операции +,-(соответственно +=,-=),++,--.

Арифметические действия с указателями учитывают тип переменной на кот. они указывают.

Эти операции изменяют адрес указателя на величину пропорциональную размеру типа данных

напр-р

float f,*pf;

/* указатель на веществ. число, в памяти вещ. занимает 4 байта */

pf=&f; / * напр-р, переменная f хранится по адресу 200, тогда pf=200

pf--; / * pf=196*/

pf+=9; / * pf=196+4*4=212*/

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

Инкремент перемещает указатель к следующему элементу массива, декремент — к предыдущему. Фактически значение указателя изменяется на величину sizeof(тип). Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер объекта данного типа, например:

Выражение (*p)++, напротив, инкрементирует значение на которое ссылается указатель.

Пример:

#include <stdio.h>

#include <conio.h>

main()

{ char *s,s1[10];

scanf("%s",s1);

s=s1;

printf("%c \n",*s);

s+=4;

printf("%c",*s);

s++;

printf("%c",*s);

s++;

printf("%c",*s);

getche();}

Операции отношения. Для указателей опр-ся след. операции отношения: = =, >=, <=, >,<, !=.

р1==р2 рез-т =1, если р1 и р2 указывают на один и тот же объект в опер. памяти

р1 >=р2 рез-т =1, если *р1 расположен в памяти правее (т.е. с большим адресом) чем р2

<=,

>

,<

, !=.

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

Указатель типа void *

Такой указатель исп-ся для доступа к любым типам данных. Указатель типа void указывает на место в операт. памяти и не содержит инф-ции о типе объекта, т.е. он указывает не на сам обьект, а на его возможное расположение. К указателю типа void не применяются арифм операции.