Информатика. Учебно-методическое пособие
.pdfОбратите особое внимание на вещественную единицу в числителе дроби. Если записать последний оператор как s = s + 1/k, то расчет будет неверен, поскольку деление будет производиться согласно арифметике целых чисел – результат деления тоже будет целым числом. Поскольку числитель дроби 1/k для всех членов ряда, кроме первого, меньше знаменателя, для них результат деления дает нуль. Поэтому итоговое значение величины s будет равно значению только первого члена ряда, т. е. единице. По этой же причине тип переменной s выбран не int, а double – чтобы не терять десятичных знаков.
Любое из трех выражений в заголовке цикла for независимо от других может быть опущено, но все символы «точка с запятой» непременно сохраняются. Так, оператор
for(;;)
{ тело цикла }
описывает бесконечный цикл. Выход из такого цикла обычно производится оператором break, который выполняется при истинности некоторого условия, проверяемого внутри тела цикла, например:
int k = 10; for(;;)
{.........
k--;
if(k) break;
}
Если опущено выражение_2, то считается, что оно истинно. В этом случае также получается бесконечный цикл.
Работа оператора for описывается такой последовательностью шагов:
1.Вычисляется выражение_1 (если оно присутствует).
2.Вычисляется выражение_2 (если оно присутствует). Если получается нуль (ложь), то цикл завершается.
3.Выполняется тело цикла.
4.Вычисляется выражение_3 (если оно присутствует).
5.Переход к п. 2.
31
Любое выражение, записываемое в заголовке цикла, может быть довольно сложным и даже представлять собой последовательность нескольких выражений, соединенных операцией «запятая»:
for(i=0; i<10; printf("\n %d",i), i+=2);
Приведенный фрагмент цикла обеспечивает вывод на экран чисел
0, 2, 4, 6, 8 «столбиком».
Предупреждение. Параметр цикла нельзя принудительно изменять внутри цикла посредством оператора присваивания, но его можно (и часто весьма удобно) использовать как простую переменную, например, как индексэлементамассива.
Оператор цикла while
Цикл for всегда можно заменить эквивалентной записью цикла while(...):
выражение_1; while(выражение_2)
{
тело цикла; выражение_3;
}
Общий вид записи оператора: while(выражение)
{ тело цикла }
Действие такого оператора заключается в многократном выполнении блока, образующего тело цикла. Этот процесс продолжается до тех пор, пока выражение, записанное в заголовке, не обратится в нуль, т. е. не станет ложным. Его истинность проверяется каждый раз перед очередным выполнением тела цикла. Поэтому если записанное в заголовке выражение изначально равно нулю, цикл не выполняется вообще. Тело цикла может составлять и единственный оператор, тогда фигурные скобки не обязательны.
32
Наш пример суммирования девяти членов ряда можно при помощи оператора while записать так:
double s = 0.; int k = 1; while(k<=9)
{
s += 1./k; k++;
}
Заметьте, что в теле цикла непременно должен быть оператор, влияющий на значение проверяемого выражения, иначе выполнение цикла никогда не прекратится.
Подобный цикл обычно записывают короче, хотя и менее наглядно:
while(k<=9)
s += 1./k++;
Если выражение в заголовке оператора всегда истинно, то получается бесконечный цикл:
while(1) {.......}
Такая конструкция имеет право на существование и используется довольно часто. Как правило, такой цикл прекращают при выполнении некоторого условия, которое записывается и проверяется внутри тела цикла. Для этой цели используется условный оператор или переключатель.
Запишем наш пример, используя бесконечный цикл: int k=1;
double s = 0.; while(1)
{
s += 1./k++;
33
if(k > 9) break;
}
Выполнение оператора break приводит к немедленному прекращению цикла и передаче управления следующему оператору программы, записанному сразу после оператора цикла.
Итак, цикл while завершается в следующих случаях:
обратилось в нуль выражение, записанное в заголовке;
в теле цикла встретились операторы break, exit;
в теле цикла выполнился оператор return;
в случае аварийного завершения программы (например, деление на нуль).
Оператор цикла do...while
Общий вид такого оператора цикла: do
{ тело цикла } while (условие);
Выполнение операторов тела цикла многократно повторяется, пока записанное условие остается истинным, т. е. ненулевым. В отличие от цикла while, здесь проверка условия на истинность производится после выполнения операторов тела цикла. Поэтому цикл обязательно выполнится хотя бы один раз, даже если проверяемое условие изначально ложно. Прекращение выполнения цикла do...while происходит в случаях, аналогичных циклу while.
Оператор continue
Может использоваться во всех трех типах циклов. Выполнение оператора continue в теле цикла вызывает досрочное прекращение текущей итерации и переход к следующей. Это равносильно безусловному переходу на закрывающую скобку тела цикла.
Следующий фрагмент обеспечивает ввод цифр вплоть до нажатия клавиши Enter.
while((ch=getch()) != 'r')
{
34
if(!isdigit(ch))
continue;
putchar(ch);
}
Можно было обойтись и без оператора continue. Достаточно было сменить проверяемое условие на противоположное:
while((ch=getch()) != 'r') if(isdigit(ch))
putchar(ch);
ПОНЯТИЕ МАССИВА. ЕГО ОБЪЯВЛЕНИЕ И ИНИЦИАЛИЗАЦИЯ
Массив – это совокупность элементов одного типа, обозначаемых единым именем и размещенных в смежных ячейках памяти.
Простейший одномерный массив можно в общем случае объявить так:
class type name[size];
Здесь class – желаемый класс памяти, type – тип элементов массива, name – имя массива, size – количество элементов в массиве. Массивы могут иметь любой класс памяти, кроме, естественно, register.
Увидев такое объявление, компилятор автоматически резервирует под массив необходимое число байт ОЗУ, равное size*sizeof(type). Содержимое выделенных ячеек памяти зависит от указанного класса памяти, который выбирается программистом точно так же, как и для простых переменных. Если указан класс памяти auto, то до первого явного присваивания выделенные ячейки содержат «мусор», при указании класса static все элементы массива автоматически обнуляются.
Если значения элементов массива известны при написании программы (т. е. являются исходными данными), то часто массив инициализируется одновременно с его объявлением:
int first[5] = {3, 7, –4, 0, 25};
Количество констант требуемого типа, перечисленных через запятую в фигурных скобках, может быть меньше, чем длина массива. То-
35
гда оставшиеся элементы инициализируются нулем. Если же численные значения получают все элементы массива, то его размерность в принципе можно и не указывать – компилятор сам ее подсчитает по количеству констант:
int first[] = {3, 7, –4, 0, 25};
Для обращения к конкретному элементу массива достаточно указать его индекс, например m = first[2]; в результате чего переменная m примет численное значение элемента first[2], равное –4. Это получи-
лось потому, что в языке Си индексы массивов начинаются с нуля!
В данном примере объявлен массив из пяти целых чисел, причем к первому из них можно обратиться как first[0], а к пятому – как first[4].
Обычно к элементам массива обращаются в цикле, число повторений которого равно, как правило, количеству элементов массива n:
for(i = 0; i < n; i++)
{
.......first[i]......
}
СВЯЗЬ МАССИВОВ И УКАЗАТЕЛЕЙ
Каждая область памяти, которую вы используете для хранения данных, имеет свой адрес. Адрес позволяет компьютеру обращаться к определенному элементу данных.
Указатель – это переменная, которая хранит адрес другой переменной определенного типа. Фактически это просто адрес одного байта оперативной памяти, с которого начинается размещение этой переменной. Переменная указатель, как и любая другая переменная, обладает именем, а также имеет тип, определяющий то, на какого рода данные она может указывать. При помощи указателей удобно выполнить многие действия:
получить доступ к содержимому любой ячейки памяти;
создать новые объекты (переменные, массивы, структуры и т. п.)
впроцессе выполнения программы путем динамического резервирования памяти;
облегчить передачу фактических параметров функциям, передавая не сам объект (который может быть довольно большим), а только его начальный адрес.
36
Как и любая переменная, каждый указатель должен быть описан и инициализирован перед использованием.
Чтобы объявить указатель, нужно выбрать для него имя, перед которым поставить звездочку, а перед звездочкой записать тип переменной или объекта, чей адрес будет храниться в указателе.
Приведем примеры объявления простейших указателей: int *p1; – указатель на целое;
char *p2; – указатель на символ;
double *p3; – указатель на вещественную переменную двойной точности.
Особенностью языка С++ является тот факт, что имя одномерного массива (без индексов и квадратных скобок!) можно рассматривать как переменную, всегда хранящую адрес начала массива, т. е. как неизменяемый указатель на первый байт участка памяти, выделенного под массив. Ему невозможно присвоить другой адрес, но продублировать этот адрес в другой указатель удобно и выгодно:
int first[] = {3, 7, –4, 0, 25}; /* Объявляем массив целых */ nt *pt; /* Вводим указатель на целое */
pt = first; /* Заносим в указатель адрес начала массива */
Занесение адреса в указатель можно было реализовать и подругому:
pt = &first[0];
Учтите, что операцию взятия адреса нельзя применять к имени массива, поскольку оно уже адекватно адресу начала массива:
pt = &first; /* Так писать нельзя ! */
Может возникнуть вполне законный вопрос: зачем произведено дублирование адреса? Ведь теперь first и pt адресуют один и тот же байт памяти. Однако имя массива по определению – это неизменяемый указатель, а указатель pt можно изменять, т. е. допустимо изменять адрес, который он содержит. Тем самым мы можем «сдвинуть» указатель так, чтобы он стал указывать на нужный элемент массива. Этот «сдвиг» происходит при выполнении над указателями разрешенных арифметических операций.
37
Арифметические операции над указателями
Над указателями (т.е. адресами) можно выполнять некоторые целочисленные арифметические операции. Чаще всего к указателю прибавляют целую константу или вычитают ее из указателя. Очень важно понять, что эти действия выполняются в единицах того типа, который указан при объявлении указателя. Так, если в программе объявлен указатель на целый объект:
int *pt;
и в него занесен некоторый адрес: pt = first;
то адрес (pt+1) будет отстоять от исходного на размер целой переменной. Если по этому адресу размещена целая величина, то доступ к ней можно получить способом разадресации указателя: *(pt+1). Заметим, что это можно реализовать и по-другому:
*(++pt).
Такая запись предписывает сначала увеличить адрес, хранимый в указателе, на sizeof(type) единиц, а затем извлечь операнд, размещенный по новому адресу.
Даже если не введен изменяемый указатель-дублер, то выражение (first+k) также определяет адрес k-го элемента, а выражение *(first+k) – содержимое по данному адресу. Никакого нарушения синтаксиса здесь нет, поскольку указатель-константа, каковым является имя массива, не изменяется, а лишь участвует в выражении на правах слагаемого. Однако записи first++ или *first++ , конечно, недопустимы.
При желании указатель, которому присвоен адрес начала массива, можно использовать вместо имени массива при обращении к элементу массива по индексу. Так, вполне корректно писать:
int first[ ] = {....}; int *pt,m;
pt = first; m = pt[3];
Последний оператор можно записать и иначе:
а) |
m = first[3]; |
б) |
m = *(first+3); |
38
Приведем текст простой программы, создающей одномерный целочисленный массив и заполняющей его первыми числами натурального ряда чисел:
int mas[10], i;
for(i = 0; i < 10; i++) mas[i] = i;
При обращении к элементам массива через указатель один из возможных вариантов таков:
int mas[10], i, *pt; pt = mas;
for(i = 0; i < 10; i++) *pt++ = i;
Конечно, можно обойтись и без указателя pt: int mas[10], i;
for(i = 0; i < 10; i++) *(mas+i) = i;
ПОРЯДОК ВЫПОЛНЕНИЯ РАБОТЫ
1.Составить схему алгоритма и предъявить ее преподавателю для проверки.
2.Написать программу двумя способами: с помощью указателей и без нее.
3.Отладить программу и получить численные результаты.
4.Оформить отчет по работе, обратив особое внимание на выводы. Подготовиться к защите, ориентируясь на контрольные вопросы. Защитить работу.
ВАРИАНТЫ ЗАДАНИЙ
Вариант 1
Дан целочисленный массив A [15]. Найти в нем два наименьших элемента.
39
Вариант 2
По заданному вещественному x вычислить значение |
x |
по следу- |
||
ющей итерационной формуле: yi 1 0.5( yi x / yi ), y0 |
x . |
Итерации |
||
прекратить при | y |
i 1 |
y | 2 10 5 . |
|
|
|
i |
|
|
|
Вариант 3
Дано положительное число a. Найти k-е число Фибоначчи такое,
что xk 1 a xk . Числа Фибоначчи: |
x1 x2 1, |
xk xk 2 xk 1, |
k = 3, 4, … |
|
|
Вариант 4
Найти количество трехзначных чисел в массиве A[12], кратных 15, но не кратных 30. Вывести их на экран монитора.
Вариант 5
Сформировать целочисленный массив A[10], элементами которого являются случайные числа из диапазона [–5; 20]. Найти среди его элементов два, разность которых имеет наибольшее значение.
Вариант 6
Вычислить, не используя функцию pow(), значения функции
z(x,m) xm sinm (x) для значений аргументов: x от –1.1 до 0.3 с шагом 0.2; m от 1 до 5 с шагом 1.
Вариант 7
Измерен рост 15 студентов. Данные записаны в массиве ROST. Разместить в массиве NR номера тех студентов, чей рост меньше 180 см, и подсчитать число таких студентов.
Вариант 8
Сформировать массив IM[100], элементами которого являются числа 1, –1, 2, –2, …, 50, –50. Сформировать массив IN[50], элементами которого являются числа 1, 3, 5, …, 49, 50, 48, 46, …, 2. Вывести их на экран монитора.
40
