книги / Практикум по программированию на языке Си
..pdfЗАДАНИЕ. Преобразуйте программу 06_16.с, сделав n и m глобальными переменными по отношению к функции main().
Задачу решает программа 06_16_2.c.
Все предложенные варианты программ, в которых создаются двумерные массивы с переменными размерами, не удовлетворяют требованиям традиционного (не стандартного) языка Си.
ЗАДАНИЕ. Напишите программу, решающую поставленную задачу с учетом ограничений традиционного Си.
Будем отдельно определять размеры (предельные) двумерного массива и той матрицы, которая должны быть размещена в этом массиве. Размеры массива зададим с помощью препроцессорных констант N и M. Размеры формируемой матрицы (значения n и m) будем вводить с клавиатуры. Ошибкой в исходных данных следует считать условие N<n или M<m. Заданию соответствует следующая программа:
/* 06_16_3.c - двумерный массив для матрицы переменных размеров */
#include <stdio.h> #define N 10 #define M 10
int main ()
{
int i, j;
int matr[N][M]; int n, m;
printf("n="); scanf("%d",&n); printf("m="); scanf("%d",&m); if (m>M || n>N)
{ printf("Error! (m>M || n>N)"
"\nm=%d, M=%d, n=%d, N=%d,",m,M,n,N); return 0;
}
for (i=0; i<n; i++) for (j=0; j<m; j++)
matr[i][j]=(i+1)+(j+1); for (i=0; i<n; i++)
{printf("\n");
for (j=0; j<m; j++)
201
printf ("matr[%d,%d]=%d\t",i+1,j+1,matr[i][j]);
}
return 0;
}
Результаты выполнения программы.
Вариант с ошибкой в исходных данных:
n=23<ENTER>
m=5<ENTER>
Error! (m>M || n>N) m=5, M=10, n=23, N=10,
Правильный ввод исходных данных:
n=4<ENTER>
m=3<ENTER>
matr[1,1]=2 matr[1,2]=3 matr[1,3]=4 matr[2,1]=3 matr[2,2]=4 matr[2,3]=5 matr[3,1]=4 matr[3,2]=5 matr[3,3]=6 matr[4,1]=5 matr[4,2]=6 matr[4,3]=7
ЗАДАЧА 06-17. Введите предельные значения вещественных
величин xmin, xmax, ymin, ymax и количества значений nx, ny, принимаемых соответственно переменными xi, yi, равномерно разме-
щенными на интервалах:
xi (xmin ≤ xi ≤ xmax ), i = 1,nx ;
yi ( ymin ≤ yi ≤ ymax ), i = 1, ny ;
Сформируйте в виде двумерного массива матрицу ║aij║с размерами nx, ny и присвойте ее элементам значения
aij = F(xi , y j ) = sin y j − cos xi .
Распечатайте по строкам полученную матрицу.
Решение, соответствующее Стандарту 99 языка Си, предлагается в следующем виде:
202
/* 06_17.c - вещественная матрица с переменными размерами */
#include <stdio.h> #include <math.h>
#define READD(VARIABLE) \ {printf(#VARIABLE"=");scanf("%le",&VARIABLE);}
#define READI(VARIABLE) \
{printf(#VARIABLE"="); scanf("%d",&VARIABLE);} int main ()
{
int nx, ny;
double xmin,xmax,ymin,ymax,x,y; READI(nx);
READI(ny);
READD(xmin);
READD(xmax);
READD(ymin);
READD(ymax);
{int i, j;
double a[nx][ny]; for (i=0; i<nx; i++) for (j=0; j<ny; j++)
{x=xmin+i*(xmax-xmin)/(nx-1); y=ymin+j*(ymax-ymin)/(ny-1); a[i][j]=sin(y)-cos(x);
}
for (i=0; i<nx; i++) { printf("\n");
for (j=0; j<ny; j++) printf("a[%d,%d]=%8.5f\t",i+1,j+1,a[i][j]);
}
}
return 0;
}
Результаты выполнения программы:
nx=5<ENTER>
ny=3<ENTER>
xmin=2.0<ENTER>
xmax=4.0<ENTER>
ymin=0.12<ENTER>
ymax=8.0<ENTER>
203
a[1,1]= 0.53586 |
a[1,2]=-0.37849 |
a[1,3]= 1.40551 |
|||
a[2,1]= 0.92086 |
a[2,2]= 0.00651 |
a[2,3]= 1.79050 |
|||
a[3,1]= 1.10970 |
a[3,2]= |
0.19536 |
a[3,3]= 1.97935 |
||
a[4,1]= |
1.05617 |
a[4,2]= |
0.14182 |
a[4,3]= |
1.92581 |
a[5,1]= |
0.77336 |
a[5,2]=-0.14099 |
a[5,3]= |
1.64300 |
Для удобного ввода исходных данных использованы макросы, предложенные в задаче 03-11 темы 3. Обратите внимание на применение библиотечных математических функций sin() и cos(). Чтобы они были доступны, в программу включен заголовочный файл math.h.
ЭКСПЕРИМЕНТ. Откомпилируйте программу 06_17.с с помощью классического компилятора, не соответствующего требованиям Стандарта 99. Убедитесь в появлении ошибок трансляции, связанных с не константными выражениями в определении размеров массива.
ЗАДАНИЕ. Модифицируйте программу 06_17.с, приведя ее в соответствие с требованиями традиционного языка Си. Например, используйте препроцессорные константы для задания размеров двумерного массива. Размеры матрицы введите с клавиатуры и выполните проверку допустимости их значений. В качестве прототипа используйте программу 06_16_3.с.
Для экспериментов с матрицами удобно иметь отдельные программы создания матриц с заданными свойствами и отдельные программы обработки матриц. В этом случае для передачи данных от программы создания к программе обработки можно использовать переназначение стандартных потоков ввода-вывода так, как мы это делали для одномерных массивов.
ЗАДАЧА 06-18. В прямоугольной вещественной матрице с размерами n на m (где n<10 и m<10) определите индексы и значение элемента, который по модулю наименее отличается от среднего арифметического всех элементов матрицы. Размеры матрицы вводите с клавиатуры, а значения ее элементов формируйте в диапазоне от –1000 до +1000 с помощью датчика псевдослучайных чисел. Решение оформите в виде двух программ: про-
204
граммы генерации значений элементов матрицы (ввод размеров и вычисление значений элементов) и программы обработки (поиск нужного элемента).
Программа генерации может использовать следующие средства стандартной библиотеки (описаны в заголовке stdlib.h): rand() и
RAND_MAX.
Функция rand() при каждом обращении возвращает псевдослучайное значение типа int из диапазона от 0 до RAND_MAX.
RAND_MAX – препроцессорная константа, определяющая предельное значение псевдослучайных целых чисел, формируемых функцией rand(). Конкретное значение RAND_MAX зависит от реализации (конкретного компилятора), но не может быть менее 32767 (требование Стандарта языка Си).
По условию задачи числовые значения элементов матрицы должны находиться в диапазоне от –1000 до +1000. Следовательно, очередное значение элемента можно вычислять по формуле:
rand()*(1000+1000)/RAND_MAX-1000
Вычисление элементов матрицы будем выполнять по "строкам", т.е. для матрицы ║xij║ последовательно определяются значения x11,
x12, …,x1m, x21,…,xn,m-1, xnm.
Программа генерации данных для формирования матрицы:
/* 06_18A.c - формирование значений элементов матрицы */
#include <stdio.h>
#include <stdlib.h> //для RAND_MAX и rand() #define N 10
#define M 10 int main ()
{
int i, j; int n, m;
double element;
printf("n="); scanf("%d",&n); printf("m="); scanf("%d",&m); if (m>M || n>N)
{ printf("Error! (m>M || n>N)"
"\nm=%d, M=%d, n=%d, N=%d,",m,M,n,N);
205
return 0;
}
printf("\n%d",n);
printf("\n%d",m); for (i=0; i<n; i++) for (j=0; j<m; j++)
{ element=2000*(double)rand()/RAND_MAX - 1000; printf("\n%8.2f",element);
}
return 0;
}
Результаты выполнения программы.
Вариант с ошибкой в исходных данных:
n=18<ENTER>
m=22<ENTER>
Error! (m>M || n>N) m=22, M=10, n=18, N=10,
Правильный ввод исходных данных:
n=2<ENTER>
m=3<ENTER>
2
3
-755.77 -806.25 -254.75 -600.99 -955.56
670.75
В тексте программы обратим внимание на следующие особенности. Чтобы были доступны функция rand() и препроцессорная константа RAND_MAX, включена директива #include<stdlib.h>. После ввода желаемых размеров матрицы выполняется сравнение значений переменных n (число строк), m (число столбцов) с препроцессорными константами N и M, которые определяют предельные размеры двумерного массива для представления матрицы в программе обработки.
206
После ввода и проверки допустимости значений переменных n и m, они выводятся в стандартный выходной поток stdin функцией printf().
Вычисление значений элементов будущей матрицы выполняются по строкам в двойном цикле. Целое значение, возвращаемое функцией rand(), явно преобразуется к типу double. Затем выполняется приведение к диапазону − 1000÷ +1000. Каждое вычисленное значение (переменная element) сразу же в теле цикла выводится в стандартный выходной поток stdin. При выводе в форматной строке функции printf() использована спецификация преобразования %8.2f. В ней f – спецификатор вывода вещественного числа в форме с фиксированной точкой. Поле вывода – 8 позиций, из них 2 позиции для цифр после десятичной точки (2 знака в дробной части числа). При этом для представления целой части со знаком отводится 5 позиций. Этого вполне достаточно, так как значения элементов матрицы по условию задачи должны быть в диапазоне от − 1000 до +1000.
Варианты выполнения программы при умалчиваемых назначениях потоков stdin, stdout показаны выше вслед за текстом программы. Переназначение стандартного выходного потока stdout, выполняемое по команде
…>test.exe>result<ENTER>
приведет к таким результатам:
на экране:
4<ENTER>
3<ENTER>
в файле result:
n=m=
4
3
-755.77 -806.25 -254.75 -600.99 -955.56
670.75
207
437.24
168.87
160.36 -582.69 714.84 -21.36
Информацию из текстового файла result будем использовать во второй программе, выполняющей формирование двумерного массива с матрицей и ее обработку. Текст программы:
/* 06_18B.c -поиск в матрице элемента с заданными свойствами */
#include <stdio.h>
#include <math.h> // для fabs() #define N 10
#define M 10 int main ()
{
int i, j;
double matr[N][M];
double sum=0.0, middle,min; int n, m, imin, jmin; scanf("%*s%d",&n); scanf("%d",&m); printf("\nn=%d",n); printf("\tm=%d",m);
if (m>M || n>N)
{ printf("Error! (m>M || n>N)"
"\nm=%d, M=%d, n=%d, N=%d,",m,M,n,N); return 0;
}
for (i=0; i<n; i++) for (j=0; j<m; j++)
{scanf("%lf",&matr[i][j]);
sum+=matr[i][j];
printf("%c[%d,%d]=%8.2f",
j == 0 ?'\n':'\t',i+1,j+1,matr[i][j]);
}
middle=sum/(n*m);
printf("\nmiddle=%8.2f",middle); min=fabs(matr[0][0]-middle);
208
for (i=0, imin=jmin=0; i<n; i++) for (j=0; j<m; j++)
if (fabs(matr[i][j]-middle)<min)
{imin=i;
jmin=j;
min=fabs(matr[i][j]-middle);
}
printf("\nimin=%d, jmin=%d, min=%8.2f", imin+1,jmin+1,matr[imin][jmin]);
return 0;
}
Результаты выполнения программы при чтении данных из файла result:
n=4 |
m=3 |
[1,2]= -806.25 |
[1,3]= -254.75 |
||
[1,1]= -755.77 |
|||||
[2,1]= -600.99 |
[2,2]= -955.56 |
[2,3]= |
670.75 |
||
[3,1]= |
437.24 |
[3,2]= |
168.87 |
[3,3]= |
160.36 |
[4,1]= -582.69 |
[4,2]= |
714.84 |
[4,3]= |
-21.36 |
|
middle= |
-152.11 |
min= -254.75 |
|
|
|
imin=1, |
jmin=3, |
|
|
В программе используется библиотечная функция fabs(), вычисляющая абсолютное значение аргумента типа double. Для обеспечения доступа к ней включен заголовочный файл <math.h>. Далее определены препроцессорные константы N и M, использованные затем в определении вещественного массива для матрицы. Переменные: sum – для суммы элементов матрицы; middle – для среднего значения элементов; min - текущее значение модуля разности между средним и очередным элементом; imin, jmin – индексы элемента со значением, ближайшим к среднему; n, m – реальные размеры матрицы, считываемые из входного потока, роль которого выполняет файл result.
В первом обращении к функции scanf() с помощью спецификации преобразования "%*s" реализуется пропуск последовательности "n=m=", затем по спецификации "%d" вводятся значения переменных n и m. После ввода значений n и m выполняется проверка допустимо-
209
сти введенных размеров матрицы. Если матрица с заданными размерами может разместиться в массиве matr[N][M], то во вложенных циклах выполняется считывание из stdin (из файла result) вещественных значений элементов матрицы. При чтении используется спецификация преобразования "%lf", предусмотренная для типа double. В том же цикле подсчитывается сумма элементов матрицы и выполняется печать ее элементов.
При выводе значений элементов каждому отводится 8 позиций, из которых две – для дробной части (спецификация %8.2f). Матрица выводится "по строкам", выравнивание расположения элементов достигается с помощью табуляции. Выбор символа ‘\n’ или ‘\t’ выполняет условное (тернарное) выражение. При истинности условия "j==0" выводится ‘\n’, т.е. выполняется переход на новую строку. Вывод выбранного символа '\n' или '\t' осуществляется по спецификации %c. Для наглядности выводятся не индексы элементов массива (начинающиеся с 0), а номера строк (i+1) и столбцов (j+1). Далее после вычисления среднего значения элементов матрицы (переменная middle) в двойном цикле анализируются значения всех элементов. Для элемента, минимально уклоняющегося от среднего значения, запоминаются уклонение (min) и индексы (imin, jmin). Значение выбранного элемента матрицы и номера строки и столбца, в которых он находится, выводятся как результат.
ЭКСПЕРИМЕНТ. В программе, формирующей значения элементов матрицы, (06_18A.с) замените спецификацию %8.2f на спецификацию %f. Оцените результат при выводе.
ЭКСПЕРИМЕНТ. В программе 06-18A.с удалите приведение типов (double) из выражения для вычисления значения переменной element. Объясните результаты исполнения.
ЭКСПЕРИМЕНТ. Подготовьте в текстовом файле с произвольным именем исходные данные, пригодные для чтения программой формирования и обработки матрицы (06_18B.с). Выполните программу, читая данные из созданного файла.
210