Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Практикум по основам программирования. Массивы. Строки. Подпрограммы. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
void main( ) { float x[n] [n1], y[n1] [n2], z[n] [n2]; int i, j, k, str, stb1, stb2 ; printf("Введите размерность матриц \n"); scanf("%d %d %d", &str, &stb1, &stb2); printf("Введите первую матрицу: "); input (x, str, stb1); printf("Введите вторую матрицу: "); input (y, stb1, stb2); MULT(x, y, z, str, stb1, stb2); printf("\n произведение матрицы x на матрицу y \n"); for ( i = 0; i < str; i++ ) { for ( j = 0; j < stb2; j++ ) printf("%f ", z[i][j] ); printf("\n"); } getch ( ); }
Здесь a, b и c – формальные параметры типа массив функции
MULT. Входными данными функции являются массивы a и b и выход-
ными массив c. Формальные параметры целого типа n, m, p – входные данные функции MULT – размерности матриц. При обращении к MULT из главной функции формальному параметру a соответствует фактический массив x, формальному b соответствует фактический y, параметру c – фактический массив z. Связь между параметрами уста­навливается по значению. Это значит, что формальному параметру a присваивается значение фактического параметра (т. е. адрес массива) x; формальному b присваивается адрес фактического массива y, а фор­мальному c адрес фактического z. Таким образом, формальные пара­метры a, b, c на самом деле параметры типа указатель на объекты типа float. Поэтому размерность формального массива по первому измере­нию можно опускать (остальные размерности не опускаются, т.к. они
61
необходимы для вычисления адреса элемента массива). При выполне­нии действий над этими параметрами из них извлекается адрес факти­ческого массива и, действия выполняются над элементами фактиче­ского массива. Таким образом, при присваивании c[i][j] значения пе­ременной s происходит обращение к элементу z[i][j] фактического массива, который и получает значение s.
Формальным параметрам n, m, p ставятся в соответствие фактиче-
ские str, stb1, stb2. Связь между этими параметрами устанавливается по значению. Это значит, что формальному параметру n присваивается значение фактической переменной str, формальной m присваивается значение фактической переменной stb1, а формальной p значение фак­тической stb2. Таким образом, в функции действия выполняются над формальными переменными n, m, p, получившими значения фактиче­ских переменных.
Прототип функции
Строгое согласование по типам между формальными и фактиче-
скими параметрами требует, чтобы до первого обращения к функции, появилось либо ее определение (см. пример программы в п.6.6.), либо ее описание, содержащее сведения о типе возвращаемого результата и о типах всех параметров. Описание функции называется прототипом функции. Синтаксис прототипа почти полностью совпадает с заголов­ком функции в ее определении:
< тип результата функции> <имя функции> ( [ <cписок формаль-
ных параметров> ] );
| void <имя функции> ( [ <cписок формальных параметров> ] ); Основные отличия: 1) в конце прототипа ставится точка с запятой,
2) имена формальных параметров можно опускать.
Пример программы с определением и прототипом функций
#include <stdio.h> #include <stdio.h> #include <conio.h > const int n = 15;
62
const int n1= 10; const int n2 = 20;
int MAX (int a, int b) // Определение функции MAX до ее вызова
{ return a > b ? a : b ; }
float cube(float x); // Прототип функции cube
void main( )
{ int max, p = 2; scanf("%d ", &max); max = MAX((int)cube((float) p), max); // Вызов функции MAX и // функции cube
printf("Наибольшее равно %d ", max); getch( );
} float cube(float x) // Определение функции cube { return x * x * x; }
Явные преобразования типов фактических параметров при обра-
щении к функции cube - (float) p и функции MAX (первый параметр) требуются для согласования типов формальных и фактических пара­метров. Прототип функции cube необходим, так как ее определение размещено после обращения к ней.
Рекурсивные подпрограммы
Рекурсия позволяет наглядно и в простых задачах достаточно ко-
ротко описать решение задачи. Но рекурсию надо использовать только в тех случаях, когда, либо природа обрабатываемых данных рекурсив­на, либо способ решения задачи является перебором возможных вари-
63
1, 1,
!
( 1)!, 1.
если n
n
nn если n

антов решения задачи. Рекурсивное решение состоит как минимум из двух частей:
1) часть, задающая непосредственное решение задачи и тем самым
определяющая возможность завершения рекурсии. Непосредственное решение возможно в каком-то частном или простом с точки зрения размера задачи случае;
2) часть, задающая решение всей задачи в целом. Эта часть являет-
ся сведением решения общей задачи к частной, т. е. к непосредствен­ному ее решению, как правило, путем снижения размерности задачи.
Механизм реализации рекурсивного решения состоит из:
1) подпрограмма явно или косвенно вызывает саму себя (и называ-
ется рекурсивная подпрограмма);
2) при каждом вызове подпрограммы создаются экземпляры ло-
кальных переменных подпрограммы, которые доступны только на том уровне рекурсии (вызове подпрограммы), на котором были созданы. Этим обеспечивается передача данных с одного уровня рекурсии на другой.
Пример рекурсивного решения задачи вычисления n! для нату-
рального n:
Определение рекурсивной функции для вычисления n!:
int fact(int n) { if (n == 1) return 1; if (n > 1) return fact ( n - 1) * n; }
64
1
2
/2
, 1,
, 1 ,
, 1 .
nn
n
x если n
x x x если n и n нечетно
x если n и n четно
 

Пример рекурсивного решения задачи вычисления xn для веще-
ственного x и натурального n:
Определение рекурсивной функции для вычисления xn:
float step(float x, int n) { if (n == 1) return x; if (n % 2) return step(x, n - 1) * x; { float r = step(x, n / 2); return r * r; } }
Пример определения рекурсивной процедуры (задача – напеча-
тать цифры, входящие в заданную последовательность символов, оканчивающуюся точкой):
void print( ) { char x; scanf("%c", &x ); if ( x >= 0 && x <= 9) { printf("%c ", x); print( ); }
65
else if (x != ′.′ ) print ( ); }
Объекты программы и их взаимодействие
В программе с многоуровневой структурой возникает проблема
взаимодействия объектов, описанных в разных частях и модулях про­граммы. В подпрограмме могут действовать три вида объектов: ло­кальные, формальные, глобальные.
Локальные объекты – это объекты, описанные в блоке. В языках с
блочной структурой принят следующий принцип локализации объек­тов: объекты, вводимые в употребление в блоке, могут использоваться только в пределах данного блока. Их область действия – оператор блок (тело подпрограммы – это оператор блок): от точки определения ло­кального объекта до конца блока. При вложенности блоков внутри блока доступны и объекты, определенные во внешних блоках. Повтор­ное определение имени во внутреннем блоке (т. е. повторение имени во внутреннем блоке) изменяет видимость объекта с таким же именем из внешнего блока: внутри блока сфера действия имен, определенных во внешних блоках сохраняется до точки их повторного описания. По­сле выхода из блока восстанавливается видимость внешних объектов, переопределенных внутри блока.
Формальные объекты – это объекты, описанные в заголовке опре-
деления подпрограммы. Область действия этих объектов – тело под­программы. Формальные параметры – это также локальные объекты подпрограммы.
Область действия формальных параметров, описанных в прототипе
функции – прототип функции.
Глобальные объекты – это объекты, описанные вне всех функций
(сама функция тоже является глобальным объектом). Сфера действия глобальных имен - модуль (файл) с текстом программы от точки опи­сания имени до конца файла. Внутри каждой функции, входящей в мо­дуль они имеют один и тот же смысл. Глобальные объекты позволяют осуществлять связь подпрограмм по данным через общее поле данных.
66
Повторное определение глобального объекта в теле функции изменяет его видимость. Внутри функции сфера действия глобальных имен со­храняется до точки их повторного описания. После выхода из функции (или внутреннего блока, содержащего повторное описание глобально­го имени) восстанавливается видимость глобальных объектов, переоп­ределенных внутри функции. Если глобальный объект определен поз­же своего первого использования, то он должен быть описан в той функции, где он используется с атрибутом extern.
Пример программы с переопределенными именами объектов
#include <stdio.h> сhar s[ ] = "СТРОКА1"; // Определение глобального объекта
void f1( ) { printf("%s", s); // Вывод значения глобальной s сhar s[ ] = "СТРОКА2"; // Определение локального объекта в блоке 1 printf("%s", s) ; // Вывод значения СТРОКА2 локальной s блока 1 scanf("%s", &s ) ; // Ввод нового значения локальной s блока 1 { сhar s[ ] = "СТРОКА3"; // Определение локального объекта блока 2
printf("%s", s ); // Вывод значения СТРОКА3 локальной s блока 2
} // Конец блока 2 printf("%s", s); // Вывод нового значения локальной s блока 1 } // Конец блока 1
void f2(char *s) { printf("%s", s); // Вывод значения формальной s блока 3 { сhar s[ ] = "СТРОКА4"; // Определение локального объекта блока 4 printf("%s", s); // Вывод значения СТРОКА4 локальной s блока 4 } // Конец блока 4 printf("%s", s) ; // Вывод значения формальной s } // Конец блока 3
67
void main ( ) { printf("%s", s ) ; //Вывод значения СТРОКА1 глобальной s в блоке 5 scanf("%s", &s ) ; //Ввод нового значения глобальной s extern int p; // Описание глобальной переменной p, определенной ниже if (p) f1( ); else f2(s); printf("%s", s) ; // Вывод значения глобальной s } // Конец блока 5
int p = 5; //Определение глобальной переменной p
Имя переменной s типа строка определено вне функций и переоп-
ределяется в функциях f1 и f2. Комментарии показывают видимость имени s в разных блоках программы.
Примеры
1. Написать программу решения задачи, выполняя ее декомпози-
цию на подзадачи:
а) Описать функцию степень (x, n) от вещественного x и натураль-
ного n, вычисляющую величину xn, и использовать ее для вычис-
ления b = 2.7k + (1 + k)-5 .
#include <stdio.h> #include <conio.h >
const int n = 15; const int n1= 10; const int n2 = 20;
float step(float x, int n)
68
{ int i; float y; for(i = 0, y = 1; i < n; i++) y *= x; return y; }
void main( ) { int k; scanf("%d", &k); float b = step(2.7, k) + 1. / step (1. + k., 5); printf(" %f ", b); getch( ); }
б) Даны координаты трех точек плоскости, определить номер ко-
ординатной четверти для каждой из трех точек (считать, что ко-
ординаты точек не совпадают с началом координат).
#include <stdio.h> #include <conio.h >
const int n = 15; const int n1= 10; const int n2 = 20;
int number(float x, float y) { int i; if (x > 0) if (y > 0) i = 1; else
69
i = 4; else if (y > 0) i = 2; else i = 3; return i; }
void main ( ) { float x1, x2, x3, y1, y2, y3; scanf("%f %f %f %f %f %f "), &x1, &y1, &x2, &y2, &x3, &y3); printf("Точка %f %f в %d четверти ", x1, y1, number ( x1, y1)); printf("Точка %f %f в %d четверти ", x2, y2, number ( x2, y2)); printf("Точка %f %f в %d четверти ", x3, y3, number ( x3, y3)); getch( ); }
в) Вычислить значение функции F(m, n) = n! m! / (n + m)! , где n и
m – неотрицательные целые числа.
#include <stdio.h> #include <conio.h >
const int n = 15; const int n1= 10; const int n2 = 20;
void main ( ) { int n, m, F; int fact(int); scanf(“%d %d”, &n, &m); F= fact(n)* fact(m) / fact(n + m);
70
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]