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

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

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

Коротко о важном

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

!Оператор, включенный в сокращенный условный оператор, выполняется только при истинности проверяемого условия

(05_01.с).

!Один их двух операторов, входящих в полный условный, всегда выполняется (05_02.с).

!В качестве условия в операторе if может использоваться арифметическое выражение (05_02.с).

!При вложении условных операторов служебному слову else соответствует ближайшее предшествующее if (05_02.c, 05_03.c, 05_06.c).

!Использование условных операторов совместно с оператором return в ряде случаев упрощает структуру программы

(05_04.с, 05_05.с).

!Тело оператора switch – это последовательность операторов, некоторые из них особым образом помечены (05_07.с, 05_08.с, 05_08_1.с).

!Оператор switch обеспечивает переход по условию к одному из последовательности помеченных операторов (05_07.с, 05_08.с).

!Сочетание оператора switch с операторами перехода (break, return, goto) позволяет выбрать одну из "параллельных ветвей", составленную из последовательности операторов (05_07.с, 05_08.с).

!Управляющее выражение переключателя должно формировать целое значение (05_07.с, 05_08.с).

!Ветвь case переключателя может быть помечена только целочисленным константным выражением (05_07.с, 05_08.с).

!Один оператор тела переключателя может быть помечен несколькими case (05_08_1.с).

!Все константы, определяющие метки переключателя, должны

быть различны (05_07.с, 05_08.с, 05_08_1.с).

!Три оператора организации циклов соответствуют трем базовым конструкциям, представляющим "повторения" при поэтапной де

161

тализации проектируемых алгоритмов (05_09.с, 05_09_1.с, 05_09_2.с).

!Выразительные возможности языка Си позволяют выходить за рамки структурного программирования (05_10.с, 05_10_1.с), но это допустимо только в небольших фрагментах программ.

!Третье из выражений, составляющих тернарное (условное), в свою очередь может быть тернарным (05_10.с).

!При вводе значений типа double в форматной строке функции scanf() используется спецификация преобразования %le, или

%lf, или %lg (05_11.с).

!"Машинный нуль" есть величина относительная, значение которой зависит от выбранного базового значения (05_11.с).

!При монотонной сходимости вычислительного алгоритма вычисления можно выполнять с точностью до машинного нуля

(05_12.с).

!При чтении символов из стандартного входного потока нужно помнить, что нажатие клавиши ENTER помещает во входной по-

ток два кода (05_13.с, 05_14.с, 05_15.с).

!В языке Си тип char отнесен к целым типам. Значение типа char может быть выведено и как символ, и как целочисленный код

(05_13.с, 05_14.с, 05_15.с).

!Переназначение стандартных потоков ввода-вывода осуществляется директивами операционной системы при запуске исполнимого модуля программы на выполнение (05_16.с, 05_17.с).

!Выражение, возвращающее значение типа char, может служить управляющим выражением в переключателе (05_17.с).

!Символьные константы могут входить в метки case (05_17.c).

!Во входном потоке, "настроенном" на чтение данных от клавиатуры, признак конца файла EOF формируется нажатием клавиш

"Ctrl+Z" (05_17.c).

!Вводя данные с клавиатуры, необходимо предусматривать условие окончания ввода, отличное от появления EOF (05_17.c), либо формировать код EOF одновременным нажатием клавиш

"Ctrl+Z"

Тема 6

Массивы

Ничто не сравнится с массивом, если нужно хранить статические табличные данные. Инициализация во время компиляции делает задачу конструирования таких массивов простой и легкой.

Б. Керниган, Р. Пайк. Практика программирования

Основные вопросы темы

!Определение и инициализация массивов.

!Массивы с переменными размерами.

!Сортировка массивов.

!Роль операторов break, continue при вложении циклов.

!Обмен данными между последовательно исполняемыми программами (вспомогательный текстовый файл).

!Представление матриц двумерными массивами.

!Инициализация многомерных массивов.

!Матрицы с переменными размерами.

6.1. Определение и обработка массивов

Массив определяется с помощью конструкции вида:

тип_элементов имя_массива[выражение];

Здесь выражение, задающее количество элементов массива, при строгом соблюдении Стандарта должно быть целочисленным кон-

стантным (integral constant expression), имеющим положительное значение. Кроме того, количество и значения элементов массива можно определить списком инициализации.

ЗАДАЧА 06-01. Определите целочисленный массив из 10 элементов. С помощью инициализации первым шести элементам

163

присвойте значения, равные их индексам. Выведите значения всех элементов массива, размещая в строке не более четырех элементов.

/* 06_01.c - инициализация массива */ #include <stdio.h>

#define N 10 int main ()

{

int i; int size;

int ar[N]={0,1,2,3,4,5}; size=sizeof(ar)/sizeof(ar[0]); for (i=0; i<size; i++)

printf("%car[%d]=%d",i%4?'\t':'\n',i,ar[i]); return 0;

}

Результат выполнения программы:

ar[0]=0 ar[1]=1 ar[2]=2 ar[3]=3 ar[4]=4 ar[5]=5 ar[6]=0 ar[7]=0 ar[8]=0 ar[9]=0

Отметим некоторые особенности программы. Количество элементов в массиве (его размер) задано препроцессорной константой N. Инициализация элементов любого массива начинается с 0, что учтено в списке инициализации. Элементы с индексами 6–9 не инициализированы. В соответствии со Стандартом языка Си они получили нулевые значения. Для "управления" размещением результатов при выводе в prinf() использовано условное выражение i%4?'\t':'\n'. Его символьное значение '\n' (когда i кратно 4) или '\t' выводится с помощью спецификации преобразования

%c.

Количество элементов в массиве в ряде случаев удобно определять выражением:

sizeof(имя_массива)/sizeof(имя_массива[0])

Именно так определено значение переменной size, которая затем используется в заголовке цикла. Вместо size в заголовке цикла можно равноправно использовать препроцессорную константу N, но явное вычисление значения size позволяет обратить внимание на

164

соотношение длины списка инициализации и размера массива, указанного в его определении.

ЭКСПЕРИМЕНТ. Используйте в списке инициализации количество элементов, превышающее N.

/* 06_01_1.c - ошибка при инициализации массива */

#include <stdio.h>

/*02*/

#define N 7

/*03*/

int main ()

/*04*/

{

/*05*/

int i;

/*06*/

int size;

/*07*/

int ar[N]={0,1,2,3,4,5,6,7,8,9};

/*08*/

size=sizeof(ar)/sizeof(ar[0]);

/*09*/

for (i=0; i<size; i++)

/*10*/

printf("%car[%d]=%d",i%4?'\t':'\n',i,ar[i]);/*11*/

return 0;

/*12*/

}

/*13*/

Предупреждающие сообщения при компиляции:

06_01_1.c: In function `main':

06_01_1.c:8: warning: excess elements in array initializer after `ar'

06_01_1.c:8: warning: excess elements in array initializer after `ar'

06_01_1.c:8: warning: excess elements in array initializer after `ar'

Результат выполнения программы:

ar[0]=0 ar[1]=1 ar[2]=2 ar[3]=3 ar[4]=4 ar[5]=5 ar[6]=6

Компилятор отметил ошибочность инициализации не существующих элементов массива ar[7]-ar[9]. Значение size равно 7, т.е. не количеству элементов списка инициализации, а значению выражения в квадратных скобках в определении массива.

165

ЭКСПЕРИМЕНТ. Попытайтесь определить массив, задав отрицательное значение препроцессорной константы N. Объясните полученные результаты компиляции.

ЗАДАЧА 06-02. Решите предыдущую задачу для массива с элементами типа char. В качестве значений первых элементов в списке инициализации используйте первые шесть букв латинского алфавита.

/* 06_02.c - инициализация символьного массива */ #include <stdio.h>

#define N 10 int main ()

{

int i; int size;

char ar[N]={'a','b','c','d','e','f'}; size=sizeof(ar)/sizeof(ar[0]);

for (i=0; i<size; i++) printf("%car[%d]=%c",i%4?'\t':'\n',i,ar[i]);

return 0;

}

Результат выполнения программы:

ar[0]=a ar[1]=b ar[2]=c ar[3]=d ar[4]=e ar[5]=f ar[6]= ar[7]= ar[8]= ar[9]=

Не инициализированные элементы типа char получают в качестве значения код, не соответствующий никакому изображаемому символу (выведены пробелы).

ЭКСПЕРИМЕНТ. Выведите на печать числовые десятичные коды тех элементов символьного массива, которые не получили явных значений при инициализации.

Задачу решает программа 06_02_1.с. Выводятся нулевые значения кодов не инициализированных элементов символьного массива.

166

В соответствии с требованием классического языка Си количество элементов при определении массива должно быть задано константным выражением. Это весьма жесткое ограничение обычно смягчается при реализациях компиляторов. Массивы переменных размеров появились в Стандарте языка Си С99 [5, 21]. Однако не все реально используемые компиляторы учитывают это нововведение. Компилятор DJGPP допускает массивы переменных размеров. Например, допустимо в выражении, определяющем размер массива, использовать переменные, глобальные по отношению к блоку, в котором определяется массив. Это позволяет использовать в программе массивы, размер которых зависит от исходных данных. Воспользовавшись этой возможностью компилятора DJGPP, решите задачу.

ЗАДАЧА 06-03. Определите массив во внутреннем блоке, используя в качестве размера массива значение переменной, определенной во внешнем блоке. Введите значения элементов массива и напечатайте их в обратном порядке.

/* 06_03.c - массив переменной длины */

 

#include <stdio.h>

/*03*/

int main ()

{

sizeArray;

/*04*/

int

/*05*/

printf("sizeArray=");

/*06*/

scanf("%d",&sizeArray);

/*07*/

{int

i, j;

/*08*/

int

ar[sizeArray];

/*09*/

for

(i=0; i<sizeArray; i++)

/*10*/

{ printf("ar[%d]=",i);

/*11*/

}

scanf("%d",&ar[i]);

/*12*/

(j=0, i=sizeArray-1; i>=0; j++, i--)

/*13*/

for

/*14*/

printf("%car[%d]=%d",j%4?'\t':'\n',i,ar[i]);/*15*/

return 0;

/*16*/

}

 

}

 

Результат выполнения программы:

sizeArray=7<ENTER>

ar[0]=2<ENTER>

ar[1]=3<ENTER>

167

ar[2]=4<ENTER>

ar[3]=5<ENTER>

ar[4]=6<ENTER>

ar[5]=7<ENTER>

ar[6]=8<ENTER>

ar[6]=8 ar[5]=7 ar[4]=6 ar[3]=5 ar[2]=4 ar[1]=3 ar[0]=2

Размер массива во внутреннем блоке определяет переменная sizeArray, значение которой вводит пользователь. При вводе значений элементов массива в функции scanf() используется адрес очередного элемента &ar[i]. Для "управления" размещением выводимой информации введена дополнительная переменная j.

ЭКСПЕРИМЕНТ. Попытайтесь задать с помощью инициализации значения элементов массива, размер которого определяется значением переменной внешнего блока. Объясните полученные результаты компиляции.

Результаты эксперимента приведены в программе 06_03_1.с. Компиляция не будет завершена. Компилятор выдает сообщение об ошибке в строке с инициализацией: "Нельзя инициализировать объект переменных размеров".

Определение массива с помощью переменных внешнего блока не соответствует действовавшему Стандарту языка Си. Этот удобный механизм включают далеко не все компиляторы. Если для трансляции программы 06_03.c использовать, например, современные компиляторы фирмы Borland или компилятор VC++6.0, то получим сообщения об ошибках. В классическом компиляторе Turbo C будут выявлены следующие отступления от правил языка:

Error C:\Practicum\…\06_03.c 9: Constant

expression required in function main()

(Вольный перевод: “В строке с порядковым номером 9 функции main() требуется константное выражение”.)

Error C:\Practicum\…\06_03.c 9: Size of structure or array not known in function main()

168

(Размер структуры или массива неизвестен в функции main().) Строка с указанным порядковым номером содержит определение массива.

Таким образом, компилятор Turbo C не допускает применения использованной схемы, когда во внешнем блоке определяется переменная, значение которой задает размер массива, определяемого во внутреннем блоке.

Решение в той постановке, которая сформулирована в условии рассмотренной задачи 06-03, невозможно при использовании большинства современных компиляторов. Однако рассмотрим более общую постановку вопроса.

ЗАДАЧА 06-04. Введите количество элементов некоторой числовой последовательности, а затем их значения. Напечатайте значения элементов в обратном порядке.

Впрограмме, решающей данную задачу, без массивов обойтись трудно – нужно сохранять значения элементов последовательности до окончания их ввода. Возможны, по крайней мере, два подхода к созданию таких программ, в которых размер массива заранее, до начала исполнения программы, неизвестен.

Первый подход – применение средств динамического распределения памяти. (Его мы рассмотрим позднее в теме 7).

Второй подход – искусственное (принудительное) ограничение количества используемых элементов массивов, определяемых в программе.

Принудительное ограничение количества используемых элементов массивов предполагает, что все массивы имеют фиксированные размеры, задаваемые при определении с помощью константных выражений. В процессе исполнения программы при решении задач не всегда используются все элементы массивов. Например, в нашей задаче можно определить массив из 100 элементов, а использовать только то количество, которое укажет пользователь, введя с клавиатуры нужное ему значение (заведомо не превышающее 100).

Вэтом случае предельные размеры массивов удобно задавать с помощью препроцессорных констант. Их же можно использовать для проверки допустимости введенного пользователем количества элементов, необходимых для решения задачи. Программа, реализующая перечисленные соглашения, может быть такой:

169

/* 06_04.c - последовательность переменной длины в массиве */

#include <stdio.h> #define SIZE_ARRAY 100 int main ()

{

int i, j, size;

int ar[SIZE_ARRAY]; printf("size="); scanf("%d",&size);

if (size > SIZE_ARRAY)

{ printf("Error! The size is greater then %d.", SIZE_ARRAY);

return 0;

}

for (i=0; i<size; i++)

{printf("ar[%d]=",i);

scanf("%d",&ar[i]);

}

for (j=0, i=size-1; i>=0; j++, i--) printf("%car[%d]=%d",j%4?'\t':'\n',i,ar[i]);

return 0;

}

Результат выполнения программы:

size=673<ENTER>

Error! The size is greater then 100.

Результат выполнения программы:

size=6<ENTER>

ar[0]=9<ENTER>

ar[1]=8<ENTER>

ar[2]=7<ENTER>

ar[3]=6<ENTER>

ar[4]=5<ENTER>

ar[5]=4<ENTER>

ar[5]=4 ar[4]=5 ar[3]=6 ar[2]=7 ar[1]=8 ar[0]=9

170