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

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

102

Программирование на языке Си

Ограничений на размерность массивов, т.е. на число индек­ сов у его элементов, в языке Си теоретически нет. Стандарт языка Си требует, чтобы транслятор мог обрабатывать опреде­ ления массивов с размерностью до 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;

{ /* тело функции */}

В настоящее время более широко используется и рекоменду­ ется стандартом языка Си определение функции, в котором спи­ сок формальных параметров объединен с их спецификацией. Структура стандартного определения функции:

типрезультата имя_функции (спецификация формальных параметров)

{

определения объектов; исполняемые операторы;

}

Соседние файлы в папке книги