СИ. Программирование на языке CC Часть Структурное программирование
Скачать 1.65 Mb.
|
# i n c l u d e 4 5 using namespace std ; 6 7 i n t main () { 8 double a , x , y , s , s0 , d ; 9 long n , i ; 10 SetConsoleOutputCP (1251); 11 cout << "Введите␣параметр␣a␣=␣"; 12 cin >> a ; 13 cout << "Введите␣число␣сомножителей␣n␣=␣"; 14 cin >> n ; 15 s = 1; 16 x = a /( a +1); // первый сомножитель коэффициент для следующих сомножителей o r ( i = 0; i < n ; i ++){ 19 cout << i +1 << "-й␣сомножитель␣=␣" << x << endl ; 20 s *= x ; // накапливаем произведение следующий сомножитель "Произведение" << s << endl ; — 76 — 24 s0 = pow ( a , n *( n +1.0)/2.0) * pow ( a +1, -( n -1.0)*( n +1.0)-1); 25 cout << "Контрольное␣значение␣=␣" << s0 << endl ; 26 d = s - s0 ; 27 i f ( fabs ( d ) < 1 e -4 ) 28 cout << "Ответ␣правильный,␣ошибка␣=␣" << d << endl ; 29 e l s e 30 cout << "Ответ␣неверный,␣ошибка␣=␣" << d << endl ; 31 Рис. 2.14. Экран программы из листинга 2.12 Суммирование бесконечных рядов. Найдём сумму бесконечного ряда 1 1! − 1 2! )︂ − 𝑥 2 (︂ 1 2! − 1 4! )︂ + 𝑥 3 (︂ 1 3! − 1 6! )︂ − . . . ± 𝑥 𝑖 (︂ 1 𝑖! − 1 (2𝑖)! )︂ ∓ . . Из курса математического анализа известно, что данный ряд является разложением функции cos √ 𝑥 − При вычислении суммы бесконечного ряда на компьютере необходимо задать точность 𝜀 — некоторое малое число (0 < 𝜀 ≪ 1). Когда очередное слагаемое станет по модулю меньше 𝜀, то вычисления можно прекратить. Словесное описание алгоритма суммирования бесконечного ряда) ввести точность 𝜀; 2) ввести значение переменной 𝑥; — 77 — 3) вычислить значение 𝑢 очередного члена ряда) 𝑠 = 𝑠 + 𝑢; 5) если |𝑢| > 𝜀, перейти к пункту 3; 6) вывести При выполнении го пункта данного алгоритма во многих случаях необходимо вычисление факториалов, степеней аргумента 𝑥 и других произведений многих сомножителей. Для ускорения вычислений выгодно использовать рекуррентные формулы. В нашем случае используется формула 𝑎 𝑖 = 1 𝑖! = 𝑎 𝑖−1 𝑖 , где 𝑎 𝑖−1 — значение этого выражения для предыдущего члена ряда. На рис. 2.15 показана схема алгоритма нахождения суммы бесконечного ряда. В данном алгоритме используются две управляющие структуры следование и цикл с постусловием. Листинг 2.13 иллюстрирует пример решения задачи, на рис. 2.16 показан результат работы программы. Листинг 2.13: Суммирование бесконечного ряда i n c l u d e # i n c l u d e # i n c l u d e # i n c l u d e i n t main (){ f l o a t x , eps ; double a , b , c , u , s , f , e ; long i ; SetConsoleOutputCP (1251); printf ("\nВычисление␣суммы␣бесконечного␣ряда\n"); printf ("Введите␣точность␣eps␣=␣"); scanf ("%g", & eps ); do { // цикл для разных значений x — 78 Введите, & x ); i = 1; a = 1.0; b = 0.5; c = x ; u = c *( a - b ); s = u ; // сначала сумма равна первому слагаемому Рис. 2.15. Схема алгоритма суммирования бесконечного ряда точность; 𝑥 — значение аргумента счётчик просуммированных членов ряда первый член ряда (𝑢) — рекуррентная формула для вычисления очередного члена ряда очередной член ряда сумма ряда — 79 — do { // цикл для слагаемых ряда i ++; // счётчик слагаемых a = a / i ; b = b / (2* i -1) / (2* i ); c = - c * x ; u = c * ( a - b ); // очередное слагаемое s += u ; // накапливаем сумму ( fabs ( u ) > eps ) && ( i < 10000) ); f = cos ( sqrt ( x )) - exp (- x ); // контрольная функция e = s - f ; // ошибка printf ("Сумма␣ряда␣при␣x=%g␣равна␣%lg\n", x , s ); printf ("истинное␣значение␣=␣%lg,␣ошибка␣=␣%lg\n", f , e ); printf ("просуммировано␣%li␣слагаемых\n", i ); printf ("Будем␣продолжать␣(y/n)?␣"); } while ( getch () == ’y’ Рис. 2.16. Результат работы программы из листинга — 80 — 2.6. Контрольные вопросы. Пояснить смысл следующий понятий, привести примеры а) условный оператор б) цикл с предусловием в) цикл с постусловием г) цикл с параметром д) условная операция. Почему не рекомендуется использовать оператор перехода goto ? 3. Чем различаются операторы ветвления if и. Какая строка будет выведена в результате выполнения следующего фрагмента программы n t a = 5, b = 12; i n t x = a && b ; i f ( x ) printf ("true"); e l s e printf ("false"); 5. Какое число будет выведено в результате выполнения следующего фрагмента программы: а) i n t a = 9, b = 2; i n t x = a <= b ? ++ a : -- б n t a = 9, b = 2; i n в n t x = 5, y = 10; y -= x --; i f ( x < y ) cout << x ; e l s г n t x = 5, y = 10; y -= ++ x ; switch ( x ) { case 5: cout << ’a’; case 6: cout << ’b’; d e f a u l t : cout << ’c’; } 6. Что будет выведено в результате выполнения следующего фрагмента программы — 81 а n t x = 5; while ( x < 8) б n t x = 5; do { cout << ++ x ; } while ( x < в o r ( i n t i =0, j =3; i < j ; где- ж o r ( i n t i =0, j =3; i < j ; j --) { cout << i ++; i f ( i < j ) continue ; cout << ’.’ << j ; } 7. Запишите уравнение прямой на плоскости. Какой смысл имеют входящие в него параметры. Как аналитически задать множество точек, лежащих ниже (выше) прямой на плоскости. Запишите уравнение окружности с заданными координатами центра и радиусом. Как аналитически задать множество точек, лежащих внутри круга с заданными координатами центра и радиусом Вне его. Сформулируйте правило окончания вычислений при суммировании бесконечных рядов — 82 — 12. Какой ряд называется знакопостоянным? Знакочередующимся. Можно ли утверждать, что отбросив при суммировании все члены ряда, начиная с первого, который по модулю меньше 𝜀, мы получим ошибку вычисления суммы ряда 𝐸 6 𝜀? 14. В листинге 2.13 через переменные 𝑎 и 𝑏 обозначены величины 1/𝑖! и 1/(2𝑖)! соответственно. Что изменится, если в переменных 𝑎 и 𝑏 хранить значения знаменателей 𝑖! и (2𝑖)!, а очередной член ряда вычислять по формуле 𝑑 = 𝑐 · (1/𝑎 − 1/𝑏)? 15. Напишите формулу Тейлора разложения функции вряд вблизи произвольной точки 𝑥 = 𝑎. 16. Можете ли вы с помощью своей программы определить, является ли сходимость ряда равномерной. Попытайтесь сформулировать правило окончания цикла при вычислении бесконечного произведения. Как можно свести задачу вычисления бесконечного произведения к задаче вычисления бесконечной суммы — 83 — 3. Массивы и указатели Массив — это упорядоченный набор однотипных элементов, доступ к которым осуществляется по их номеру (индексу. Одномерные статические массивы При описании переменной-массива в квадратных скобках указывается количество элементов. Стандарты C89 и C90 языка C требуют, чтобы количество элементов задавалось в виде константы или константного выражения (значение которого можно вычислить на этапе компиляции. Пример описания одномерного массива из десяти целых элементов n или i n t n = 10; i n или d e f i n e N 10 i n Рекомендуется использовать второй вариант. Элементы массивов нумеруются, начиная с 0; поэтому в приведённых выше примерах элементы будут иметь индексы от 0 до Элементы одномерного массива располагаются в памяти последовательно, друг за другом, в соответствии сих индексами, без пропусков. Новый стандарт разрешает в качестве размера локального массива (т. е. объявленного внутри некоторого блока, ограниченного фигурными скобками, например, внутри какой-либо функции, в том числе функции указывать переменную целого типа, значение которой уже известно: 1 Компилятор Microsoft Visual C++ пока не поддерживает стандарт C99. Массивы с переменной длиной (Variable Length Array, VLA) можно использовать с компиляторами GNU Compiler Collection (GCC) или MinGW, например, в Dev C++. — 84 — long n ; cout >> "Введите␣число␣элементов␣=␣"; cin >> n ; double arr [ n ]; Разумеется, если значение переменной после этого изменится, то размер массива arr всё равно останется прежним. В описании массива можно использовать инициализатор = { 2, 6, 3, 1, 4, 2, 1 } Здесь первым семи элементам массива (с индексами от 0 до 6) присвоены начальные значения, остальные три элемента (с индексами от 7 до 9) по умолчанию инициализируются нулями. При использовании инициализатора размер массива в квадратных скобках можно опустить (оставив сами скобки) число элементов массива будет совпадать с числом значений, указанных в фигурных скобках. Также допускается не указывать размер переменной-мас- сива, если она является формальным параметром функции (см. раздел 4.1 ). Для доступа к отдельным элементам массива в квадратных скобках указывают нужный индекс, например = 10; a [8] = a [0] + a [4] При выполнении программы не производится никаких автоматических проверок выхода индекса за допустимые пределы, что может привести к ошибкам при небрежном программировании. Пример 3.1. Сумма элементов массива. В листинге 3.1 приведён пример программы, которая запрашивает с клавиатуры фактический размер массива, значения его элементов и вычисляет их сумму и среднее значение на рис. 3.1 показан результат работы программы. Листинг 3.1: Вычисление суммы элементов массива i n c l u d e 2 # i n c l u d e 3 using namespace std ; — 85 — 4 5 i n t main (){ 6 const i n t m =10; // размер массива массив и среднее значение элементов n t n ; // число элементов 11 do { 12 cout << "Введите␣число␣элементов␣" 13 "(не␣больше␣" << m << ")␣=␣"; 14 cin >> n ; 15 } while ( n > m ); // пока не будет введено правильное значение 17 double sum = 0.0; // здесь будем накапливать сумму элементов o r ( i n t i =0; i < n ; i ++) { 19 cout << "␣␣введите␣элемент␣[" << i << "]␣=␣"; 20 cin >> a [ i ]; // вводим й элемент сумма элементов "Сумма␣введённых␣элементов␣=␣" << sum << endl ; 24 x = sum / n ; // среднее арифметическое "Среднее␣значение␣=␣" Рис. 3.1. Экран программы из листинга — 86 Пример 3.2. Максимальный и минимальный элементы массива. В листинге 3.2 приведён пример программы, в которой находятся максимальный и минимальный элементы массива. Сначала переменная max ини циализируется наименьшим, а переменная min — наибольшим возможным числом для типа double . Константа DBL_MAX определена в заголовочном файле. Затем мы перебираем все элементы массива в цикле, и, если очередной элемент больше текущего значения максимума (или меньше текущего значения минимума, то запоминаем новое значение максимума (минимума, а также номер позиции или Листинг 3.2: Максимальный и минимальный элементы массива i n c l u d e 2 # i n c l u d e 3 using namespace std ; 4 5 i n t main (){ 6 const i n t m =10; // размер массива массив n t n ; // число элементов, "rus"); 10 cout << "Введите␣число␣элементов␣" 11 "(не␣больше␣" << m << ")␣=␣"; 12 cin >> n ; 13 i f ( n > m ){ 14 cout << "Введённое␣число␣слишком␣велико,\n" 15 "будет␣использовано␣значение␣" << m << endl ; 16 n = m ; 17 } 18 double max = - DBL_MAX , min = DBL_MAX ; 19 i n t imax = -1, imin = -1; 20 f o r ( i n t i =0; i < n ; i ++) { 21 cout << "␣␣введите␣элемент␣[" << i << "]␣=␣"; 22 cin >> a [ i ]; // вводим й элемент — 87 — 23 i f ( a [ i ] > max ) { 24 max = a [ i ]; imax = i ; 25 } 26 i f ( a [ i ] < min ) { 27 min = a [ i ]; imin = i ; 28 } 29 } 30 cout << "Максимальный␣элемент␣" << max 31 << "␣с␣номером␣" << imax << endl ; 32 cout << "Минимальный␣элемент␣" << min 33 << "␣с␣номером␣" << imin << endl ; 34 system ("pause"); 35 return 0; 36 } 3.2. Указатели. Динамические массивы Для создания различных структур данных произвольного размера (в том числе, массивов) используют специальную динамическую память, которая предоставляется программам операционной системой. Переменные, значения которых хранятся в динамической памяти, называются динамическими. Они не имеют имени, доступ к ним производится с помощью указателей. Указатели. Указатель — это переменная, которая хранит адрес некоторой области памяти. При описании указателя задаётся тип значения, на которое он должен указывать, и ставится звёздочка. Например, описание указателя на целое значение: int * p1 ; описание указателя на вещественное число двойной точности: double * p2 ; В одной строке можно описать и обычную переменную, и указатель, например, те. звёздочка относится к имени переменной, а не к названию типа. Независимо от типа указателя, он занимает в памяти фиксированный размер байта). Описав переменную-указатель, мы можем присвоить ей адрес другой переменной, используя оператор взятия адреса &. Например Указателю также можно присвоить адрес, хранящийся в другом указателе, если их типы совместимы: p3 = p1 ; Указателю можно присвоить произвольный адрес в памяти, например (Считается, что никакая переменная не может иметь нулевой адрес, поэтому указатель, имеющий значение 0, никуда не указывает или p = Чтобы получить доступ к области памяти, адрес которой хранит указатель, используется операция разыменования (разадресации) — звёздочка перед именем переменной-указателя: i n t i , j ; i n t * p1 (& i ); // p1 указывает на i * p1 = эквивалентно i = 5; j = эквивалентно j = Можно описать указатель без определённого типа (если тип значения заранее неизвестен или указатель требуется использовать для работы со значениями разного типа): void * p ; но тогда каждый раз при разыменовании придётся использовать операцию приведения типа. Например & i ; j = (Пример использования модификатора const при описании указателей — 89 — const i n t k = 5; // целая константа i n t * p1 ; // указатель на целую константу n t * const p2 = & i ; // константный указатель на целую переменную i n t * const p3 = & k ; // константный указатель на целую константу К указателям можно применять арифметические операции сложения, вычитания, инкремента и декремента. Если, например, описать указатель на длинное целое и присвоить ему адрес какой-либо переменной i n t p = то после выполнения операции p += адрес, хранящийся в указателе, увеличится на 4 · 2 = 8 (т. к. одна переменная типа long int занимает в памяти 4 байта). Указатели и одномерные массивы. Имя массива рассматривается компилятором как константный указатель на нулевой элемент массива. Поэтому после описания, например, массива int a[10] запись *(a+i) эквивалентна обращению к i -му элементу массива, т. е. a[i] Пример 3.3. Копирование элементов из одного массива в другой n t a [] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, b [10]; // Первый способ o r ( i n t i = 0; i < 10; i ++) b [ i ] = a [ i ]; // Второй способ o r ( i n t i = 0; i < 10; i ++) *( b + i ) = *( a + i ); // Третий способ — 90 — i n t * p1 , * p2 ; f o r ( i n t i = 0, p1 = a , p2 = b ; i < 10; i ++) *( p2 ++) = Присваивание массивов не допускается. Если мы хотим скопировать элементы одного массива в другой, то должны сами сделать это в цикле. Ссылки. Ссылка — это синоним имени, которое указывается при её инициализации. Другими словами, ссылка — это указатель, который не требуется разыменовывать. Например n t k ; // целая переменная n t & r = k ; // ссылка на переменную k i n t i = r + 5; // тете ссылка на константу-символ Обычно ссылки используются в качестве выходного параметра функции (см. раздел, с. 117 ). Выделение и освобождение динамической памяти. В приведённых выше примерах переменные-указатели хранили адреса других именованных переменных. Но обычно указатели используются для работы с безымянными данными, которые хранятся в динамической памяти. Для выделения в динамической памяти области для хранения безымянной переменной используется операция в С) или функция в языке С). Например, для создания динамической переменной целого типа: |