Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Практикум по основам программирования. Массивы. Строки. Подпрограммы. Учебное пособие
.pdf
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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
