книги / Программирование на языке Си
..pdf102 |
Программирование на языке Си |
Ограничений на размерность массивов, т.е. на число индек сов у его элементов, в языке Си теоретически нет. Стандарт языка Си требует, чтобы транслятор мог обрабатывать опреде ления массивов с размерностью до 31. Однако чаще всего ис пользуются одномерные и двумерные массивы. Продемон стрируем на простых вычислительных задачах некоторые прие мы работы с массивами.
Вычисление среднего и дисперсии. Введя значение п из диапазона (0<п<=100) и значения п первых элементов массива х[0], х[1],...,х[п-1], вычислить среднее и оценку дисперсии зна чений введенных элементов массива. Задачу решает следующая программа:
1/* Вычисление среднего и дисперсии */
2#include <stdio.h>
3void main ( )
4{ /*п - количество элементов */
5int i,j,n;/*b-среднее,d-оценка дисперсии; */
6 float a,b,d,x[100],e; /*a ,е-вспомогательные*/
7while (1)
8{
9printf("\n Введите Значение n=");
10scanf("%d", fin);
11if( n > 0 fifi n <= 100 ) break;
12printf("\n Ошибка! Необходимо 0<n<101 ");
13} /* Конец цикла ввода значения п */
14printf("\n Введите Значения элементов:\п");
15for( b=0.0,i=0; i<n; i++)
16{
17printf("x[%d] =", i);
18scanf("%f", fix[i]);
19b+=x[i];/* Вычисление суммы элементов */
20}
21b/=n;/* Вычисление среднего */
22for(j=0,d=0.0; j<n; j++)
23{
24a=x[j]-b;
Глава 2. Вввдение в программирование на языке Си |
103 |
25d+=a*a;
26} /* Оценка дисперсии*/
27d/=n;
28printf("\n Среднее =%f, дисперсия=%£",b,d);
29}
Впрограмме определен (строка 6) массив х со 100 элемента ми, хотя в каждом конкретном случае используются только пер вые п из них. Ввод значения п сопровождается проверкой допустимости вводимого значения (строки 7-ь13). В качестве условия после while записано заведомо истинное выражение 1, поэтому выход из цикла (оператор break) возможен только при вводе правильного значения п, удовлетворяющего неравенству 0<п<101. Следующий цикл (строки 15-5-20) обеспечивает ввод п элементов массива и получение их суммы (Ь). Затем в цикле (строки 22-ь26) вычисляется сумма d квадратов отклонений эле ментов от среднего значения. Возможен следующий результат работы программы:
Введите |
значение п=5 |
|
Введите |
значение элементов: |
|
х[0] |
= |
4 |
х[1] |
= |
5 |
х [2] |
= 6 |
|
х[3] |
= |
7 |
х[4] |
= |
6 |
Среднее=6.000000, дисперсия=2.000000
Вложенные циклы. В теле цикла разрешены любые испол нимые операторы, в том числе и циклы, т.е. можно конструиро вать вложенные циклы. В математике вложенным циклам соответствуют, например, кратные суммы или произведения.
В качестве примера рассмотрим фрагмент программы для получения суммы такого вида:
10 |
5 |
5 |
104 |
Программирование на языке Си |
Введем следующие обозначения: а - двумерный массив, со держащий значения элементов матрицы; р - произведение эле ментов строки матрицы; с - сумма их значений; s - искомая сумма (результат). Опустив определения переменных и опера торы ввода-вывода, запишем текст на языке Си:
double а[10][5];
for( s=0.0,j=0; j<10; j++)
{
for( p=l.0,c=0.0,i=0; i<5; i++)
{
P * = a [ j ] [ i ] ; c+=a[j][i];
>
s+=c+p;
}
При работе с вложенными циклами следует обратить внима ние на правила выполнения операторов break и continue. Каж дый из них действует только в том операторе, в теле которого он непосредственно использован. Оператор break прекращает выполнение ближайшего внешнего для него оператора цикла. Оператор continue передает управление на ближайшую внеш нюю проверку условия продолжения цикла.
Для иллюстрации рассмотрим фрагмент другой программы для вычисления суммы произведений элементов строк той же матрицы:
|
|
10 |
5 |
1 |
double |
а[10][5]; |
|
2 |
for (j=0,s=0.0; j<10; j++) |
||
3 |
{ |
(i=0,p=1.0; |
i<5; i++) |
4 |
for |
||
5 |
{ |
if (aCj][i] == 0.0) break; |
|
6 |
|
||
7 |
|
p*=a[j][i]; |
|
6 |
} |
|
|
9 if (i <5) continue;
10 s+=p;
11 }
Глава 2. Введение в программирование на языке Си |
105 |
Внутренний цикл no i прерывается, если (строка 6) обнару живается нулевой элемент матрицы. В этом случае произведе ние элементов столбца заведомо равно нулю, и его не нужно вычислять. Во внешнем цикле (строка 9) проверяется значение i. Если i<5, т.е. элемент a[j][i] оказался нулевым, то оператор continue передает управление на ближайший оператор цикла (строка 4), и, таким образом, не происходит увеличение s (стро ка 10) на величину "недосчитанного" значения р. Если внутрен ний цикл завершен естественно, то i равно 5, и оператор continue не может выполняться.
Упорядочение в одномерных массивах. Задаче упорядоче ния или сортировке посвящены многочисленные работы мате матиков и программистов. Для демонстрации некоторых особенностей вложения циклов и работы с массивами рассмот рим простейшие алгоритмы сортировки. Необходимо, введя значение переменной 1 <n<= 100 и значения п первых элементов массива а[0],а[1],...,а[п-1], упорядочить эти первые элементы массива по возрастанию их значений. Текст первого варианта программы:
/* Упорядочение элементов массива */■ #include <stdio.h>
main( )
{
int n ,i ,j ; double a[100],b; while(1)
{
printf("\n Введите количество элементов n=") ; scanf("%d",&n);
if (n > 1 && n <= 100) break; printf("Ошибка! Необходимо 1<п<=100!");
)
1 printf("\n Введите значения элементов " "массива:\п");
for(j=0; j<n; j++)
{
printf("a[%d]=", j+1); scanf("%lf",&a[j]);
}
106 |
Программирование на языке Си |
for(i=0; i<n-l; i++) for(j=i+l; j<n; j++)
if(a[i]>a[j])
{
b=a[i];
a[i]=a[j]; a [j ] = b;
>
printf("\n Упорядоченный массив: \n"); for(j=0; j<n; j++)
printf("a[%d]=%f\n",j+l,a[j]);
>
Результаты выполнения программы:
Введите количество элементов п=-15 Ошибка! Необходимо 1<п<=100! Введите количество элементов п=3 Введите значения элементов массива: а[1] = 88.8
а[2] = -3.3
а[3] = 0.11 Упорядоченный массив:
а[1] = -3.3 а[2] = 0.11 а[3] = 88.8
Обратите внимание, что при заполнении массива и при печа ти результатов его упорядочения индексация элементов выпол нена от 1 до п, как это обычно принято в математике. В программе на Си это соответствует изменению индекса от 0 до
(п-1).
В программе реализован алгоритм прямого упорядочения - каждый элемент a[i], начиная с а[0] и кончая а[п-2], сравнива ется со всеми последующими, и на место a[i] выбирается мини мальный. Таким образом, а[0] принимает минимальное зна чение, а[1] - минимальное из оставшихся и т.д. Недостаток это го алгоритма состоит в том, что в нем фиксированное число сравнений, не зависимое от исходного расположения значений элементов. Даже для уже упорядоченного массива придется вы полнить то же самое количество итераций (п-1)*п/2, так как
Глава 2. Вввдение в программирование на языке Си |
107 |
условия окончания циклов не связаны со свойствами, т.е. с раз мещением элементов массива.
Алгоритм попарного сравнения соседних элементов позволя ет в ряде случаев уменьшить количество итераций при упорядо чении. В цикле от 0 до п-2 каждый элемент a[i] массива сравнивается с последующим a[i+l] (0<i<n-l). Если a[i]>a[i+l], то значения этих элементов меняются местами. Упорядочение заканчивается, если оказалось, что a[i] не больше a[i+l] для всех i. Пусть к - количество перестановок при очередном просмотре. Тогда упорядочение можно осуществить с помощью такой по следовательности операторов:
do {
for (i=0,k=0; i<n-l; i++) if ( a[i] > a[i+1] )
{
|
b=a[i]; |
|
a[i]=a[i+l]; |
|
a[i+l]=b; |
|
k=k+l; |
> |
• |
n— |
; |
>
while( k > 0 );
Здесь количество повторений внешнего цикла зависит от ис ходного расположения значений элементов массива. После пер вого завершения внутреннего цикла элемент а[п-1] становится максимальным. После второго окончания внутреннего цикла на место а[п—2] выбирается максимальный из оставшихся элемен тов и т.д. Таким образом, после j-ro выполнения внутреннего цикла элементы a[n-j],...,a[n-l] уже упорядочены, и следующий внутренний цикл достаточно выполнить только для 0<i<(n-j-l). Именно поэтому после каждого окончания внутреннего цикла значение п уменьшается на 1.
В случае упорядоченности исходного массива внешний цикл повторяется только один раз, при этом выполняется (n -1) срав нений, к остается равным 0. Для случая, когда исходный массив упорядочен по убыванию, количество итераций внешнего цикла
108 |
Программирование на языке Си |
равно (п—1), а внутренний цикл последовательно выполняется (п-1)*п/2 раз.
Имеется возможность улучшить и данный вариант алгоритма упорядочения (см., например, Кнут Д. "Искусство программи рования. ТЗ. Сортировка и поиск". М.: Мир, 1980), однако рас смотренных примеров вполне достаточно для знакомства с особенностями применения массивов и индексированных пере менных.
Инициализация массивов. При определении массивов воз можна их инициализация, т.е. присваивание начальных значе ний их элементам. По существу (точнее по результату), инициализация - это объединение определения объекта с одно временным присваиванием ему конкретного значения. Исполь зование инициализации позволяет изменить формат определения массива. Например, можно явно не указывать ко личество элементов одномерного массива, а только перечислить их начальные значения в списке инициализации:
double d[ ]={1.0, 2.0, 3.0, 4.0, 5.0};
В данном примере длину массива компилятор вычисляет по количеству начальных значений, перечисленных в фигурных скобках. После такого определения элемент d[0] равен 1.0, d[l] равен 2.0 и т.д. до d[4], который равен 5.0.
Если в определении массива явно указан его размер, то коли чество начальных значений не может быть больше количества элементов в массиве. Если количество начальных значений меньше, чем объявленная длина массива, то начальные значения получат только первые элементы массива (с меньшими значе ниями индекса):
int М[8]={8,4,2};
В данном примере определены значения только переменных М[0], М[1] и М[2], равные соответственно 8, 4 и 2. Элементы М [3],..., М[7] не инициализируются.
Правила инициализации многомерных массивов соответст вуют определению многомерного массива как одномерного, элементами которого служат массивы, размерность которых на
Глава 2. Введение в программирование на языке Си |
109 |
единицу меньше, чем у исходного массива. Одномерный массив инициализируется заключенным в фигурные скобки списком начальных значений. В свою очередь, начальное значение, если оно относится к массиву, также представляет собой заключен ный в фигурные скобки список начальных значений. Например, присвоить начальные значения вещественным элементам дву мерного массива А, состоящего из трех "строк" и двух "столбцов", можно следующим образом:
double А[3][2]={{10,20>, (30,40), {50,60}};
Эта запись эквивалентна последовательности операторов присваивания: А[0][0]=10; А[0][1]=20; А[1][0]=30; А[1][1]=40; А[2][0]=50; А[2][1]=60;. Тот же результат можно получить с одним списком инициализации:
double А[3][2]={10,20,30,40,50,60} ;
С помощью инициализаций можно присваивать значения не всем элементам многомерного массива. Например, чтобы ини циализировать только элементы первого столбца матрицы, ее можно описать так:
double Z[4][6]={{1}, {2}, (3), {4}};
Следующее описание формирует "треугольную" матрицу в целочисленном массиве из 5 строк и 4 столбцов:
int х[5][4]={{1), (2,3), (4,5,6), {7,8,9,10} };
В данном примере последняя пятая строка х[4] остается не заполненной. Первые три строки заполнены не до конца. Схема размещения элементов массива изображена на рис.2.5.
Строка |
|
|
0 |
|
|
|
1 |
|
|
|
2 |
|
|
|
3 |
|
|
4 |
|
Столбец |
0 |
1 |
2 |
3 |
0 |
1 |
2 |
3 |
0 |
1 |
2 |
3 |
0 |
1 |
2 |
3 |
о и |
2 |
3 |
Значение |
1 |
- |
- |
- |
2 |
3 |
- |
- |
4 |
5 |
6 |
- |
7 |
8 |
9 |
10 |
- |- |
- |
- |
Рис. 2.5. Схема "треугольного" заполнения матрицы
110 |
Программирование на языке Си |
2.5. Ф ункции
Определение функций. Как уже было отмечено, каждая программа на языке Си - это совокупность функций. В опреде лении функции указываются последовательность действий, вы полняемых при обращении к функции, имя функции, тип результата (возвращаемого значения) и совокупность формаль ных параметров, заменяемых при обращении к функции факти ческими параметрами (аргументами).
"Описание и определение функций - это та область, где стандартом ANSI в язык внесены самые существенные измене ния" [2]. Так как многие компиляторы были написаны до ввода стандартов, а многие современные компиляторы позволяют ис пользовать два варианта определения и описания функций, не будем ограничиваться только вариантом, рекомендуемым стан дартом языка. Это целесообразно, так как "достандартный" (нестандартный) вариант часто встречается в опубликованных ранее текстах программ и нужно уметь их читать. Начнем в ис торической последовательности с нестандартного определения функции, однако отметим еще раз, что стандарт допускает два варианта определения.
Действия, выполняемые при обращении к функции, задает ее тело - составной оператор (блок), обязательным элементом ко торого служат внешние фигурные скобки { }. Имя функции, тип результата, совокупность параметров и их свойства задают за головок функции. Структура классического (нестандартного) определения функции:
типрезультата имя_функции (список формальных_параметров)
спецификация_формалъных_параметров
{
определения объектов исполняемые операторы
}
Глава 2. Введение в программирование на языке Си |
111 |
Здесь первые три строки - заголовок, последние четыре - тело функции.
В этой главе будем рассматривать функции, возвращающие значения целого или вещественного типа. Таким образом, типом результата может быть char, int, long, float, double или long double. Могут бьггь добавлены signed и unsigned. Если тип ре зультата не указан, то по умолчанию предполагается целый тип int. Допустимы функции, не возвращающие результата. В этом случае для них должен быть использован тип void.
Имя неглавной функции - это произвольно выбираемый идентификатор. Имя функции не должно совпадать со служеб ными словами, именами библиотечных функций и с другими именами в программе. Список формальных параметров - набор идентификаторов, отделяемых друг от друга запятыми. Специ фикация формальных параметров определяет их свойства и во многом подобна определениям и описаниям. В данной главе будем считать, что параметр может быть специфицирован либо как простая переменная целого или вещественного типа, либо как одномерный массив (целый или вещественный).
Пример рассмотренного определения функции:
double f (a,b,d) int a;
float b; double d;
{ /* тело функции */}
В настоящее время более широко используется и рекоменду ется стандартом языка Си определение функции, в котором спи сок формальных параметров объединен с их спецификацией. Структура стандартного определения функции:
типрезультата имя_функции (спецификация формальных параметров)
{
определения объектов; исполняемые операторы;
}