WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!


Pages:     | 1 |   ...   | 4 | 5 || 7 | 8 |   ...   | 13 |

if (scanf("%d", &a) != 1) // если данные не удалось присвоить переменной, scanf("%*s"); // то выбросить их в виде строки.

scanf("%f", &b);

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

Таким образом, не смотря на достаточно большие возможности scanf(), эта функция хранит в себе ряд неудобств и опасностей.

Решение задач Задание 1. На прошлом занятии вы написали программу, содержащую функции, вычисляющие факториал числа и заданный элемент ряда Фибоначчи. Измените эту программу таким образом, чтобы она запрашивала у пользователя, что он хочет вычислить: факториал или число Фибоначчи. Затем программа запрашивала бы у пользователя либо число для вычисления факториала, либо номер элемента ряда Фибоначчи.

2. Напишите программу, которая запрашивает у пользователя две даты в формате дд.мм.гггг.

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

3. Используя цикл, напишите код, в котором пользователю предлагается вводить данные до тех пор, пока он не сделает это корректно, т.е. пока все указанные в scanf() переменные не получат свои значения. Протестируйте программу. Что вы наблюдаете и почему Как можно решить проблему Урок 10. Псевдослучайные числа Функции rand() и srand() В языках программирования обычно предусмотрены функции, позволяющие генерировать случайные числа в определенном по умолчанию диапазоне. На самом деле генерируются не случайные, а так называемые псевдослучайные числа; они выглядят случайно, но вычисляются по вполне конкретной формуле. Но для простоты далее мы все равно будем называть их случайными.

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

Задание Напишите программу, в которой целочисленной переменной присваивается результат выполнения функции rand(). Выведите значение переменной на экран.

Функция rand() возвращает целое число от 0 до значения присвоенного константе RAND_MAX. Значение RAND_MAX зависит от системы и определено в заголовочном файле stdlib.h. Так, например, оно может быть равно 32767 (двухбайтовое целое) или (четырехбайтовое целое).

Задание Определите значение RAND_MAX в вашей системе. Для этого не забудьте подключить к файлу исходного кода заголовочный файл stdlib.h.

Код ниже выводит на экран 50 случайных чисел:

#include #include main () { char i;

for (i = 1; i <= 50; i++) { printf("%15d", rand());

if (i % 5 == 0) printf("\n");

} } В теле цикла осуществляется переход на новую строку после каждых выведенных на экран пяти чисел. Для этого используется выражение, в котором находится остаток от деления i на 5, результат сравнивается с 0. Чтобы после первого числа не происходил переход на новую строку, i сначала присваивается единица, а не ноль (т.к. 0 делится на 5 без остатка).

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

Вы должны были заметить, что при каждом запуске программы числа остаются одинаковыми. Даже если вы перекомпилируете программу, результат не изменится. Данный эффект связан с тем, что начальное (инициализирующее) число, которое подставляется в формулу вычисления первого и последующих псевдослучайных чисел, для каждой системы всегда одно и то же. Однако это начальное число можно изменить с помощью функции srand(), которой в качестве параметра передается любое целое число. Понятно, что если вы зададите конкретный аргумент для функции, например, srand(1000), то от вызова к вызову программы числа будут также одни и те же. Хотя и не те, что были бы без srand(). Поэтому появляется проблема, как сделать так, чтобы аргумент дляsrand() был тоже случайным Получается замкнутый круг.

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

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

Значит значение для srand(), преобразованное в целое из системного времени, будет различным.

Текущее время можно узнать с помощью функции time(), прототип которой описан в файле time.h. Передав time() в качестве параметра NULL, мы получим целое число, которое можно передать в srand():

srand(time(NULL));

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

Получение целых случайных чисел в заданных диапазонах Функция rand() выдает случайное число от 0 до значения RAND_MAX. Что делать, если требуется получать случайные числа в иных диапазонах, например, от 100 до 999 Сначала рассмотрим более простую ситуацию: получить случайные числа от 0 до 5. Если любое целое число попытаться разделить на 5 нацело, то можно получить как 0 (когда число делится на 5 без остатка), так и 1, 2, 3, 4. Например, rand() вернула число 283. Применяя к этому числу операцию нахождения остатка от деления на 5, получим 3. Т.е. выражение rand() % 5 дает любое число в диапазоне [0, 5).



Однако, что если надо, чтобы число 5 так же входило в диапазон, т.е. диапазон имеет вид [0, 5] Логично предположить, что следует найти остаток от деления на 6. При этом более грамотным будет следующее рассуждение: надо находить остаток от деления на размер диапазона. В данном случае он равен шести значениям: 0, 1, 2, 3, 4, 5. Чтобы найти размер диапазона надо из допустимого максимума вычесть допустимый минимум и прибавить единицу: max - min + 1. Будьте внимательны: если, например, требуется, чтобы указанный в задаче максимум не входил в диапазон, то единицу прибавлять не надо или надо вычитать единицу из максимума.

Задание Напишите программу, выдающую 50 случайных чисел от 0 до 99 включительно.

Итак, мы знаем формулу получения длины диапазона: max - min + 1. Если требуется получить число от 6 до 10 включительно, то длина диапазона будет равна 10 - 6 + 1 = 5.

Выражение rand() % 5 даст любое число от 0 до 4 включительно. Но нам надо от 6 до 10. В таком случае достаточно к полученному случайному остатку прибавить 6, т.е. минимум.

Другими словами, надо выполнить сдвиг. Действительно для приведенного примера:

– если остаток был равен 0, то добавляя 6, получаем 6;

– остаток 1, добавляем 6, получаем 7;

– … – остаток 4, прибавляем 6, получаем 10;

– остатка больше 4 не может быть.

В таком случае формула для получения случайного числа в диапазоне [a, b] выглядит так:

rand() % длина_диапазона + сдвиг, где длина_диапазона вычисляется как b - a + 1, сдвиг является значением a.

В эту формулу также вписываются случаи, когда необходимо получить случайное число от до N, т.е. они являются ее частными случаями.

Задание Выведите на экран ряд случайных чисел, принадлежащих диапазону от 100 до включительно.

С таким же успехом можно получать случайные отрицательные числа. Действительно, если диапазон задан как [-35, -1], то его длина будет равна -1 - (-35) + 1 = 35, что соответствует действительности; выражение получения случайного числа будет выглядеть так:

rand() % 35 - Так, если остаток от деления составил 0, то мы получим -35, а если 34, то -1. Остальные остатки дадут значения в промежутке от -35 до -1.

Задание Выведите на экран ряд случайных чисел, принадлежащих диапазону от -128 до включительно.

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

Поясним вторую причину. Допустим диапазон задан как [2.50, 5.30]. Он состоит не из определенного количества чисел (как в случае целых), а из неопределенного (можно сказать, бесконечного) числа значений, т.к. вещественные числа можно представлять с различной степенью точности. Позже выполняя округление все равно будет шанс получить максимальную границу диапазона, поэтому для вычисления длины диапазона достаточно из максимума вычесть минимум.

Если разделить случайное число, преобразованное к вещественному типу, которое выдала функция rand(), на значение константыRAND_MAX, то получится вещественное случайное число от 0 до 1. Теперь, если это число умножить на длину диапазона, то получится число, лежащее в диапазоне от 0 до значения длины диапазона. Далее если прибавить к нему смещение к минимальной границе, то число благополучно впишется в требуемый диапазон.

Таким образом формула для получения случайного вещественного числа выглядит так:

(float) rand() / RAND_MAX * (max - min) + min Задание Заполните массив случайными числами в диапазоне от 0.51 до 1.00. Выведите значение элементов массива на экран.

Равновероятные случайные числа Функция rand() генерирует любое случайное число от 0 до RAND_MAX с равной долей вероятности. Другими словами, у числа 100 есть такой же шанс выпасть, как и у числа 25876.

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

#include #include #define N main () { int i;

int arr[5] = {0};

srand(time(NULL));

for (i=0; i < N; i++) switch (rand() % 5) { case 0: arr[0]++; break;

case 1: arr[1]++; break;

case 2: arr[2]++; break;

case 3: arr[3]++; break;

case 4: arr[4]++; break;

} for (i=0; i < 5; i++) printf("%d - %.2f%%\n", i, ((float) arr[i] / N) * 100);

} В приведенной программе массив из пяти элементов сначала заполняется нулями.

Случайные числа генерируются от 0 до 4 включительно. Если выпадает число 0, то увеличивается значение первого элемента массива, если число 1, то второго, и т.д. В конце на экран выводится процент выпадения каждого из чисел.

Задание Спишите данную программу. Посмотрите на результат ее выполнения при различных значениях N: 10, 50, 500, 5000, 50000. Объясните увиденное.

Урок 11. Особенности массивов при работе с указателями К указателям можно прибавлять целые числа Рассмотрим программу:

#include #define N main () { int arrI[N], i;





for (i=0; i

} Создается массив arrI, далее в цикле for выводятся значения адресов ячеек памяти каждого элемента массива. Результат выполнения программы будет выглядеть примерно так:

0x7ffffbff0x7ffffbff0x7ffffbff0x7ffffbff405c 0x7ffffbffОбратите внимание на то, что значение адреса каждого последующего элемента массива больше значения адреса предыдущего элемента на 4 единицы. В вашей системе эта разница может составлять 2 единицы. Такой результат вполне очевиден, если вспомнить, сколько байтов отводится на одно данное типа int, и что элементы массива сохраняются в памяти друг за другом.

Теперь объявим указатель на целый тип и присвоим ему адрес первого элемента массива:

int *pI;

pI = &arrI[0];

Цикл for изменим таким образом:

for (i=0; i

Здесь к значению pI, которое является адресом ячейки памяти, прибавляется сначала 0, затем 1, 2, 3 и 4. Можно было бы предположить, что прибавление к pI единицы в результате дает адрес следующего байта за тем, на который указывает pI. А прибавление двойки вернет адрес байта, через один от исходного. Однако подобное предположение не верно.

Вспомним, что тип указателя сообщает, на сколько байт простирается значение по адресу, на который он указывает. Таким образом, хотя pI указывает только на один байт (первый), но "знает", что его "собственность" простирается на все четыре (или два). Когда мы прибавляем к указателю единицу, то получаем указатель на следующее значение, но никак не на следующий байт. А следующее значение начинается только через 4 байта (в данном случае).

Поэтому результат выполнения приведенного цикла с указателем правильно отобразит адреса элементов массива.

Задание Убедитесь в этом сами.

Прибавляя к указателям (или вычитая из них) целые значения, мы имеем дело с так называемой адресной арифметикой.

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

Присвойте указателю адрес четвертого элемента, затем, используя цикл, выведите на экран адреса 4, 5 и 6-ого элементов массива.

Имя массива содержит адрес его первого элемента Да, это именно так, данный факт следует принять как аксиому. Вы можете убедиться в этом выполнив такое выражение:

printf("%p = %p\n", arrI, &arrI[0]);

Отсюда следует, что имя массива – это ничто иное, как указатель. (Хотя это немного особенный указатель, о чем будет упомянуто ниже.) Поэтому выражения pI = &arrI[N] и pI = arrI дают одинаковый результат: присваивают указателю pI адрес первого элемента массива.

Раз имя массива — это указатель, ничего не мешает получать адреса элементов вот так:

for (i=0; i

Соответственно значения элементов массива можно получить так:

for (i=0; i

Примечание. Если массив был объявлен как автоматическая переменная (т.е. не глобальная и не статическая) и при этом не был инициализирован (не присваивались значения), то в нем будет содержаться "мусор" (случайные числа).

Получается, что запись вида arrI[3] является сокращенным (более удобным) вариантом выражения *(arr+3).

Взаимозаменяемость имени массива и указателя Если имя массива является указателем, то почему бы не использовать обычный указатель в нотации обращения к элементам массива также, как при обращении через имя массива:

int arrI[N], i;

int *pI;

pI = arrI;

for (i=0; i

Отсюда следуют выводы. Если arrI — массив, а pI — указатель на его первый элемент, то пары следующих выражений дают один и тот же результат:

– arrI[i] и pI[i];

– &arrI[i] и &pI[i];

– arrI+i и pI+i;

– *(arrI+i) и *(pI+i).

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

Указателю pI можно присвоить адрес любого из элементов массива. Например, так pI = &arrI[2] или так pI = arr+2. В таком случае результат приведенных выше пар выражений совпадать не будет. Например, когда будет выполняться выражение arrI[i], то будет возвращаться i-ый элемент массива. А вот выражение pI[i] уже вернет не i-ый элемент от начала массива, а i-ый элемент от того, адрес которого был присвоен pI. Например, если pI был присвоен адрес третьего элемента массива (pI = arr+2), то выражение arrI[1] вернет значение второго элемента массива, а pI[1] — четвертого.

Задание Присвойте указателю (pI) ссылку не на первый элемент массива (arrI). В одном и том же цикле выводите результат выражений arrI[i] и pI[i], где на каждой итерации цикла i для обоих выражений имеет одинаковое значение. Объясните результат выполнения такой программы.

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

Это значит, что выражение pI = arrI допустимо, а arrI = pI нет. Имя массива является константой. При этом не надо путать имя массива (адрес) и значения элементов массива.

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

Pages:     | 1 |   ...   | 4 | 5 || 7 | 8 |   ...   | 13 |










© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.