Вы находитесь на странице: 1из 83

Содержание

Введение ………………………………………………………………………………... 4
Тема № 1.
“Типы данных в C. Функции ввода, вывода“ …………………………….. 5
Тема № 2.
“Операции, операнды, выражения“ ………………………………………... 16
Тема № 3.
“Операторы” ……………………………………………………………………….. 23
Тема № 4.
“Поинтеры” ……………………………………………………………………….... 34
Тема № 5.
“Функции обработки символьных последовательностей” …………...... 43
Тема № 6.
“Типы данных, определяемые пользователем” ………….................... 53
Тема № 7.
“Аргументы функции main()“………………………….......………………….. 66
Тема № 8.
“Рекурсии“………………………………………………………………………….. 70
Тема № 9.
“Сортировка данных”……………………………………………………………. 76
Приложение 1.
“Спецификаторы формата” ............................................................... 84
Литература …………………………………………………………………………...... 85

3
Введение

Дисциплину “Programarea calculatoarelor” студенты факультета


“Cibernetică, Statistică şi Informatică Economică” изучают в 1 семестре,
специализации “Cibernetică şi Informatică Economică” и “Statistica” и в 1 и 2
семестрах, специализация “Informatică Economică” и “Informatică Economică şi
Limbi Moderne Aplicate”. Курс предусматривает изучение одного языка
программирования.
Данная работа представляет собой сборник задач для практических и
лабораторных работ по языку программирования С. Структурно она
организована как набор лабораторных работ которые охватывают все темы
соответствующей аналитической программы и преследует цель помочь
студентам в изучении языка программирования С и получении практических
навыков программирования на этом языке.
Предлагаются следующие лабораторные работы:

№ Название работы Часы


1 Типы данных в C. Функции ввода, вывода 4-12
2 Операции, операнды, выражения 4-12
3 Операторы 4-12
4 Поинтеры 4-12
5 Функции обработки символьных последовательностей 4-12
6 Типы данных, определяемые пользователем 4-12
7 Аргументы функции main() 2-8
8 Рекурсии 2-4
9 Сортировка данных 2-8

Каждая тема содержит ряд указаний, которые имеют целью в сжатой


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

4
Лабораторная работа N 1

Тема: Типы данных в С. Функции ввода, вывода

Цель работы: Изучение базовых типов данных в языке С, функций


ввода, вывода, их использование для ввода и вывода данных под
управлением форматов и без них.

Указания:

Любая программа обрабатывает некоторые данные и в результате этой


обработке получаются другие, новые данные.

Переменные и постоянные данные. Данные некоторой программы


могут быть постоянными или переменными. Постоянные данные это
константы в обычном смысле слова. Значение константы определяется
символами своего представления и не может быть изменено. Таковыми
являются, например, числа 3, -2, 2003, 1.2, 3.141592653, -123.456, символы
‘a’, ‘S’, ‘%’, “Informatica”, “CiBeR” и др.
Чтобы задать символьную константу, записываем необходимый символ
между двумя апострофами: ‘a’, ‘4’, ‘&’, ‘;’ и т.д. По-другому записывается
символ апостроф. Перед ним ставится символ backslash: ‘\’’. Таким же
образом представляются символ backslash и символ кавычки: ‘\\’, ‘\”’.
Для того чтобы задать константу символьная последовательность,
записываем соответствующую последовательность в кавычках: “Arborele
Lumii”.
Числовые константы пишутся обычным образом: 5, -12, 65432, 3.14,
123.456, 0.314E+1 и т.д.
Переменные данные, в отличие от постоянных данных, могут менять
свои значения в процессе работы программы. Во время выполнения,
программа хранит данные в памяти компьютера. Переменные данные
хранятся в именованные зоны памяти. Такая именованная локация (зона)
памяти называется переменной. Имя переменной образовано из букв, цифр и
символа подчеркивания ‘_’. Первым символом имени не может быть цифра.
Каждая переменная принадлежит некоторому типу данных. Тип
переменной указывает какого рода значения может она хранить и какие
операции программа может выполнить с этими значениями. Тип определяет
и размер памяти в которой будут храниться значения переменной. Во время
выполнения программы, значения переменной могут меняться, а тип
переменной не может меняться. Константа, помимо своего значения также
имеет тип. Но тип константы, как и ее значение, определяется символами из
ее записи и не может быть изменен.

Базовые типы данных в языке С. Типы данных указываются


ключевыми словами. В языке С базовыми типами данных являются: char,
int, float, double. Переменная типа char хранит один символ, например
букву. Символ хранятся в памяти своим кодом ASCII, который является
5
целым числом. Неявно, диапазоном значений для переменной типа char
являются все целые числа от -128 до 127. Одно значение типа char
хранится в памяти в одном байте.
Все переменные любой программы должны быть объявлены. Чтобы
объявить одну или более переменных, записываем имя нужного типа и имя
необходимых переменной или переменных: char car; char rac, arc; и т.п.
Присваивание некоторого значения переменной можно произвести с
помощью операции присваивания ‘=’: car=’С’; rac=’i’; arc=’b’; и т.д.
Компилятор, ”видя” объявление char car; выделяет один байт памяти для
переменной char и фиксирует имя переменной и адрес выделенной для нее
зоны памяти. Когда компьютер выполняет операцию присваивания car=’С’;
он записывает в зону памяти, выделенной для переменной car, значение
символьной константы ’С’, т.е. число 67 – код ASCII для символа ’С’.
Переменная типа int хранит целые значения. Диапазон значений и
“размер” для типа int зависят, вообще, от компьютера. Как правило, в
системах, с длиной машинного слова в два байта, значение типа int
хранится в двух байтах, а диапазон значений для такого типа данных - все
целые числа от -32768 до 32767. Вообще, размер типа int совпадает с
размером машинного слова.
Переменной можно присвоить значение при ее объявлении: int an=2003,
luna; Здесь, при объявлении переменной an, ей присваивается значение
2003, а о значении переменной luna нельзя ничего сказать до первого
присваивания ей какого-нибудь значения.
Тип float используется для хранения рациональных чисел, у которых
имеется и дробная часть. Числа хранятся с обычной (ординарной) точностью,
т.е. 7 точных цифр. Компилятор выделяет 4 байта для переменной такого
типа. Диапазон значений - числа от 1.7∙10-38 до 1.7∙10+38.
Для хранения рациональных чисел с большим количеством точных цифр
используется тип double. Значение такого типа хранится в 8 байтах.
Числа хранятся с двойной точностью, т.е. до 15 точных цифр. Диапазон
значений - числа от 1.7∙10-308 до 1.7∙10+308.
Для представления рациональных чисел, особенно когда они довольно
“длинные”, используется запись с порядком (научная запись). Например,
длина в километрах одного светового года - 9460800000000. В научной
записи это число можно представить различными способами: 9.4608Е12,
94.608е11, 0.94608Е13 и т.д. Заглавная буква Е, как и прописная буква е,
обозначает множитель являющийся степенью числа 10. Например, записи
3.14Е0, 0.314е1, 31.4Е-1, 314.0е-2 представляют одно и то же число 3.14 .
Напомним, что форма научной записи некоторого числа , в которой целая
часть (в двоичной записи) есть одна ненулевая цифра, называется
нормализованной формой числа.
Модификаторы типа. В языке С имеются типы данных “родственные”
базовым типам. Они образуются модификацией базовых типов. Существуют
следующие модификаторы типа: short ( короткий ), long ( длинный ),
signed ( со знаком ), unsigned ( без знака ). Таким образом,
“родственниками” типа int являются типы short int, long int, signed int,
unsigned int, long signed int, long unsigned int.
Модификаторы signed и unsigned относятся к целым значениям.
Первый допускает, а второй запрещает отрицательные значения. Известно,
что в представлении целого отрицательного или положительного числа в
6
памяти компьютера, самый старший бит выделяется для хранения знака
числа. Для типа unsigned этот “знаковый” бит используется как и остальные
биты для двоичных цифр числа. Поэтому, соответствующий диапазон чисел
увеличивается в два раза.
Обычно, типы char и signed char совпадают. Следовательно, диапазон
значений для типа unsigned char это числа от 0 до 255. А “размер” типа,
также как и для char есть 1 байт.
Как правило, совпадают и типы int и signed int. Диапазон для типа
unsigned int это числа от 0 до 65535. А “размер” типа, также как и для
int есть ( обычно ) 2 байта.
Модификатор long может быть применен к типам int, unsigned int и
double. Для long int и unsigned long int слово int можно не писать:
пишут long и unsigned long соответственно. Порядок записи
модификаторов ( long unsigned или unsigned long ) не имеет значения.
Если “размер” типа int есть 2 байта, тогда для long он 4 байта. Если же int
занимает 4 байта, то long занимает 4 байта.
Современные компиляторы С допускают тип long double для чисел с
плавающей точкой. Значение такого типа хранится в 10 байтах с точностью
до 18 цифр. Диапазон для него - числа от 3.4Е-4932 до 1.1Е+4932 .
Известно, что тип постоянной определяется ее представлением. Так,
константа 314 есть значение типа int, а константа 45678 есть значение
типа long, т.к. ее значение выходит за пределы диапазона значений для
типа int. Можно явно указать тип long для таких констант как, 314. Для
этого, в представлении константы, за последней цифрой записываем букву l
или L: 314l или 314L. Это относится и к модификатору unsigned. Буква
u или U в конце константы определяет для нее тип unsigned. Допускается
комбинация букв l или L с u или U ( порядок не имеет значения ).

Комментарии. В программу можно ввести комментарии. Комментарий


это замечание, текст, который поясняет некоторые моменты связанные с
программой. Он может быть записан в нескольких строчках. Комментарий
начинается символами /*, которые открывают комментарий и
заканчивается символами */, которые закрывают его. Между символами ‘/’
и ‘*’ не допускаются другие символы.

Функция clrscr() стирает экран. Она не имеет параметров.


Прототип функции void clrscr( void ); содержится в файле <conio.h>.

Функция getch() читает без эхо символ со стандартного устройства ввода


прямо в память, без нажатия клавиши Enter. При чтении символа ASCII
функция возвращает код прочитанного символа. При чтении символа не
ASCII функция вызывается дважды: при первом вызове она возвращает
значение 0, а при втором – код нажатой клавиши. Функция не имеет
параметров.
Как правило, функция getch() используется и для прерывания
выполнения программы и высвечивания окна пользователя до нажатия
какой-либо клавиши.
Прототип функции int getch( void ); содержится в файле <conio.h>.

7
Функция getche( ) читает с эхо символ со стандартного устройства ввода
прямо в память, без нажатия клавиши Enter. При чтении символа ASCII
функция возвращает код символа. При чтении символа не ASCII функция
вызывается дважды: при первом вызове она возвращает значение 0, а при
втором – код нажатой клавиши. Функция не имеет параметров.
Прототип функции int getche( void ); содержится в файле <conio.h>.

Функция putch( e ) выводит символ на стандартное устройство вывода.


Она имеет один параметр. Значение выражения е определяет выводимое
изображение. При значении е из интервала [32;126] функция выводит
печатаемый символ с соответствующим кодом. При других значениях е
функция выводит графическое изображение, если значение е соответствует
графическому символу. Если значение е соответствует неграфическому
символу, то функция либо не выполняет никакого действия, либо выполняет
действие соответствующее этому коду. Например, putch( 10 ), как и
putch( ‘\n’ ), переводит курсор на следующую строку в тот же столбец.
Функция возвращает код выводимого изображения, то есть значение е.
Прототип функции int putch( int c ); содержится в файле <conio.h>.

Функция getchar() читает с эхо символы ASCII со стандартного


устройства ввода в специальный буфер до нажатия клавиши ENTER. При
нажатии клавиши ENTER, функция возвращает код текущего символа из
буфера. При повторном вызове она возвращает код следующего символа из
буфера. Если в буфере уже нет данных, вызов функции требует ввода с
клавиатуры в буфер новой последовательности символов. Ввод символов с
помощью буфера позволяет исправить ошибки допущенные при вводе до
нажатия клавиши ENTER.
Прототип функции int getchar( void ); содержится в файле <stdio.h>.

Функция putchar( e ) печатает символ ASCII на стандартное устройство


вывода. Она возвращает код выводимого символа, или –1 при ошибке. В
данном случае putchar( ‘\n’ ) как и putchar( 10 ) переводит курсор на
начало следующей строки.
Прототип функции int putchar( int e ); содержится в файле <stdio.h>.

Функция gets( s ) читает с эхо со стандартного устройства ввода


последовательность символов ASCII, заканчивающуюся нажатием клавиши
<Enter>. Параметром функции является адрес начала зоны памяти куда
помещается прочитанная последовательность символов. Как правило в
качестве параметра используется имя одномерного массива типа char. При
нажатии клавиши ENTER функция записывает в память символ NUL ( ‘\0’ ),
означающий конец последовательности, т.е. ‘\n’ заменяется на ‘\0’.
Каждый символ занимает в памяти один байт, значит последовательность из
n символов занимает n+1 байт. Функция возвращает адрес начала зоны
памяти, где хранится введенная последовательность, т.е. адрес первого
символа. Если обнаруживается символ EOF, функция возвращает значение
0.
Прототип функции содержится в файле <stdio.h>.

8
Функция puts( s ) выводит на стандартное устройство вывода
последовательность символов ASCII заканчивающуюся символом NUL ( ‘\0’ ).
Она имеет один параметр, являющийся адресом начала выводимой
последовательности. Как правило, в качестве парамера используется имя
одномерного массива типа char. При обнаружении символа NUL, функция
не выводит его, а переводит курсор на начало следующей строки, т.е. символ
‘\0’ заменяется на ‘\n’. Функция возвращает код последнего символа
последовательности, т.е. символа предшествующего NUL, или –1 при
ошибке.
Прототип функции содержится в файле <stdio.h>.

Функция printf( c, p1, p2, p3, … ) выводит на стандартное устройство


вывода данные под управлением форматов. Имеет один или несколько
параметров. Как правило, параметр c – последовательность символов в
кавычках, которая содержит выводимые тексты и спецификаторы форматов
выводимых данных. Тексты, содержащиеся в параметре c выводятся без
изменения. Последовательности escape, например '\n’, ’\t’, ‘\0’ и др. не
выводятся, а “выполняются”. Параметры p1, p2, p3,… являются
выражениями, значения которых выводятся. Каждому такому параметру в
последовательности символов c соответствует спецификатор формата по
которому выводятся соответствующие значения. Спецификатор формата
начинается знаком % и заканчивается одной или двумя специальными
буквами ( см. Приложение 1 ). Он определяет преобразование выводимого
значения из внутреннего формата во внешний. Параметры p1, p2, p3,…
могут отсутствовать. Тогда выводятся только тексты из параметра c.
Спецификатор формата, помимо специальных букв, может содержать и
другие символы. Эти символы имеют свое предназначение.
Последовательность десятичных цифр определяет длину поля, в котором
выводится значение. Неявно, выводимое значение выравнивается по
правому краю поля вывода, а неиспользованные позиции поля слева
остаются незаполненными. Если указанная длина поля вывода недостаточна,
тогда эта длина игнорируется и выводимое значение занимает необходимое
количество позиций. Знак минус определяет выравнивание по левому краю
поля вывода. В этом случае, неиспользованные позиции поля вывода слева,
остаются незаполненными.
Цифра 0 определяет заполнение цифрой 0 неиспользованных позиций
поля вывода слева.
Функция возвращает число выведенных байт ( символов ) или –1 при
ошибке.
Прототип функции содержится в файле <stdio.h>.

Функция scanf( c, p1, p2, p3, … ) вводит со стандартного устройства


ввода данные под управлением форматов. Имеет несколько параметров. Как
правило, параметр с – последовательность символов в кавычках,
содержащая спецификаторы форматов для вводимых данных. Она может
содержать и пробелы, которые игнорируются. Параметры р1, р2, р3, …-
адреса зон памяти, где будут храниться вводимые данные. Адрес зоны
памяти обычно выражается оператором & перед именем простой или
индексированной переменной. Каждому из этих параметров в
последовательности символов с соответствует спецификатор формата по
9
которому вводится соответствующее значение. Спецификатор формата
начинается знаком % и заканчивается одной или двумя специальными
буквами ( см. Приложение 1 ). Он определяет преобразование вводимого
данного из внешнего формата во внутренний.
Функция читает все данные, которые соответствуют спецификаторам
форматов из параметра с. Если какое-либо значение не соответствует
спецификатору формата то ввод прекращается. Функция возвращает
количество правильно введенных данных.
Прототип функции содержится в файле <stdio.h>.

Функция fflush( stdin ) стирает содержимое буфера клавиатуры


( очищает буфер ).
Прототип функции содержится в файле <stdio.h>.

Функция exit( c ) прерывает выполнение программы. Параметр с


определяет состояние программы в момент вызова функции. Как правило,
значение 0 параметра с определяет нормальное состояние завершения
выполнения программы, а отличное от нуля значение означает наличие
ошибки. Используется для завершения выполнения программы.
Прототип функции содержится в файле <stdlib.h> и в файле
<process.h>.

Операция sizeof определяет размер в байтах для данного или для типа
данных. Например, sizeof( int ) возвращает количество байт, необходимое
для хранения значения типа int, а sizeof( d ) возвращает количество байт
выделенных для данного с именем d.

Примеры программ:

Пример 1.1. Программа вводит используя, getch(), символ ASCII и затем


выводит этот же символ, используя putch().

#include <conio.h>
#include <stdio.h>
void main(void)
{ char a;
clrscr();
a=getch();
putch(a);
getch();
}

Пример 1.2. Программа читает используя, getch(), символы не


соответствующие коду ASCII и затем выводит код каждого символа,
используя printf().

#include <conio.h>
#include <stdio.h>
void main(void)
10
{ char a;
clrscr();
getch(); // Primul apel
printf("%d\n",getch()); // Al doilea apel
getch(); // Primul apel
a=getch(); // Al doilea apel
printf("%d\n",a);
getch(); // Primul apel
printf("%d\n",a=getch()); // Al doilea apel
getch();
}

Пример 1.3. Программа читает прописную букву и выводит символ,


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

#include <conio.h>
#include <stdio.h>
void main(void)
{ char c;
clrscr();
c=getchar();
putchar(c-1);
getch();
}

Пример 1.4. Программа читает последовательность символов и выводит


ее длину.

#include <conio.h>
#include <stdio.h>
#include <string.h>
void main(void)
{ char s[256];
clrscr();
gets(s);
printf(“Şirul are %d caractere”,strlen(s));
getch();
}

Пример 1.5. Программа читает три целых числа и выводит их среднее


арифметическое с двумя цифрами после запятой.

#include <conio.h>
#include <stdio.h>
void main(void)
{ int a,b,c;
clrscr();
scanf(“%d %d %d”,&a,&b,&c);
printf(“a=%d\tb=%d\tc=%d\nmedia=%.2f”,
a,b,c,(a+b+c)/3.0); //media cu 2 zecimale
11
getch();
}

Пример 1.6. Программа печатает между двумя символами ’*’ константу


123.456f ( определенную директивой #define ), с помощью различных
спецификаторов форматов для данных типа float.

#include <conio.h>
#include <stdio.h>
#define a 123.456f
void main(void)
{ clrscr();
printf("*%f*\n",a);
printf("*%2f*\n",a);
printf("*%20f*\n",a);
printf("*%-20f*\n",a);
printf("*%020f*\n",a);
printf("*%.2f*\n",a);
printf("*%.10f*\n",a);
printf("*%2.2f*\n",a);
printf("*%2.10f*\n",a);
printf("*%20.10f*\n",a);
printf("*%-20.10f*\n",a);
printf("*%020.10f*\n",a);
getch();
}

Содержание работы:

Составьте программы для нижеследующих заданий. Запишите программы


на сервер и в тетрадь.

1.0. Наберите и выполните программы для примеров 1.1-1.6.

1.1. Программа читает с эхо печатаемый символ ( с помощью getche() ) и


выводит его на экран ( с помощью putch() ), переводит курсор на
следующую строку ( с помощью putch('\n') ), высвечивает окно пользователя
( с помощью getch() ) и ожидает нажатия какой-либо клавиши.
Что произойдет если при чтении нажать клавишу, не соответствующую
печатаемому символу или символу ASCII?

1.2. Программа читает символ АSCII ( с помощью getchar() ) и выводит


его на экран ( с помощью putchar() ), переводит курсор на следующую
строку ( с помощью putchar(‘\n’) ), высвечивает окно пользователя и
приостанавливает выполнение программы ( с помощью getch() ) до нажатия
какой либо клавиши.
Что происходит, если при чтении нажать на клавишу, не
соответствующую символу ASCII?
Каково различие между putch( `\n` ) и putchar( `\n` ) ?
12
1.3. Программа читает фамилию и имя студента ( с помощью gets() ),
выводит ( с помощью putchar() ) соответствующие инициалы в одну строку
( после каждой буквы выводит точку ), выполняет newline ( с помощью
putchar(`\n`) ), и выводит сообщение “Для продолжения нажмите любую
клавишу” и ожидает ( с помощью getch() ) нажатия какой-либо клавиши.

1.4. Программа читает ( с помощью getchar() ) символ , выводит его ( с


помощью printf() ) на экран между двумя символами ‘*’, выполняет
newline, выводит окно пользователя и ожидает нажатия какой-либо
клавиши.

1.5. Программа читает ( с помощью getchar() ) символ, выводит его ( с


помощью printf() ) на экран между двумя символами ‘%’, выполняет
newline, выводит окно пользователя и приостанавливает выполнение
программы до нажатия какой-либо клавиши.

1.6. Программа выводит на экран между двумя символами ‘*’ текст


“Limbajul C++” по следующим форматам: %s, %15s, %-15s, %10s, %15.10s,
%-15.10s.

1.7. Программа читает символ ASCII ( с помощью getchar() ) и выводит ( с


помощью printf() ) прочитанный символ и его код на экран.

1.8. Программа читает символ, не соответствующий коду ASCII ( с


помощью getch() ) и выводит ( с помощью printf() ) его код на экран.

1.9. Программа выводит константу 12345, заданную с помощью #define,


в 10-й, 8-й и 16-й системах счисления.

1.10. Программа выводит константу 123456789, определенную с


помощью #define, в 10-й, 8-й и 16-й системах счисления.

1.11. Программа выводит между двумя символами ‘*’ константу 123


( определенную с помощью #define ) по следующим форматам: %d, %2d,
%10d, %-10d, %010d, %ld, %u, %lu.
Сравните результаты.

1.12. Программа выводит между двумя символами ‘*’ константу 123.456


( определенную с помощью #define ) по различным форматам для данных
типа double.
Используйте буквы f, e, E, g, G, lf, le, lG, lE, lg.
Сравните с примером 1.6.
Объясните результаты.

1.13. Программа выводит между двумя символами ‘*’ константу 123.456l


( определенную с помощью #define ) по различным форматам для типа long
double.
Используйте буквы Lf, Le, LE, Lg, LG. Попробуйте использовать и другие
буквы.
13
Сравните с примером 1.6 и программой пункта 1.12.
Объясните результаты.

1.14. Программа читает ( с помощью scanf() ) символ и выводит ( с


помощью printf() ) прочитанный символ и его код ASCII на экран.

1.15. Программа читает ( с помощью scanf() ) фамилию и имя студента,


разделенные пробелами, затем выводит ( с помощью puts() ) фамилию в
одной строке, а имя в другой.

1.16. Программа читает ( с помощью gets() ) фамилию и имя студента,


затем выводит ( с помощью printf() ) фамилию в одной строке, а имя в
другой. Что происходит, если фамилия и имя на входе разделены
пробельными символами, а не нажатием клавиши Enter?

1.17. Программа читает ( с помощью scanf() ) десятичное целое,


состоящее из не более 4 цифр и выводит ( с помощью printf() ) его в 10-й,
8-й и 16-й системах счисления.

1.18. Программа читает ( с помощью scanf() ) десятичное целое,


состоящее из не более 9 цифр и выводит ( с помощью printf() ) его в 10-й,
8-й и 16-й системах счисления.

1.19. Программа читает ( с помощью scanf() ) календарную дату ddmmyy


( последовательность из 6 цифр ) и выводит ее по формату 20yy/mm/dd.
Здесь dd, mm, yy являются двузначными числами, представляющими день,
месяц и год соответственно.

1.20. Программа читает ( с помощью scanf() ) заглавную букву и выводит (


с помощью printf() ) соответствующую ей прописную букву.

1.21. Программа определяет с помощью sizeof и выводит на экран


размер памяти, отведенной каждому типу данных ( char, signed char,
unsigned char, short, signed short, unsigned short, int, signed int,
unsigned int, long int, long signed int, long unsigned int, float, double,
long double ).

Контрольные вопросы:

1.1. Какова процедура запуска системы Borland C++ ?


1.2. Как запускается на выполнение программа в Borland C++ ?
1.3. Как высвечивается окно пользователя в Borland C++ ?
1.4. В каких файлах содержатся прототипы стандартных функций?
1.5. Как вывести на экран help для стандартной функции, например для
clrscr() ?
1.6. Что представляет собой операция ввода ( чтения )?
1.7. Что представляет собой операция вывода ( записи )?
1.8. Какие функции читают с эхо?
1.9. Какие функции читают без эхо?
14
1.10. Какие функции ввода требуют нажатия клавиши Enter?
1.11. Какие функции ввода не требуют нажатия клавиши Enter?
1.12. Каковы различия между getche() и getchar() ?
1.13. Каковы различия между putch() и putchar() ?
1.14. Какие значения возвращают функции ввода?
1.15. Какие значения возвращают функции вывода?
1.16. Какие функции читают данные прямо в память?
1.17. Какие функции читают данные используя буфер?
1.18. Как реагируют функции ввода на нажатие клавиш Enter, ^z, ^c,
F1, BS, Esc etc.?
1.19. Как реагируют функции ввода на символы ‘\n’, ’\t’, ’\a’, ^z, ^c,
EOF, NUL etc.?
1.20. Как можно вывести код клавиши, не соответствующей символу
ASCII?
1.21. Что возвращает функция с заголовком main() ?
1.22. Что возвращает функция с заголовком void main(void) ?
1.23. Как вывести с помощью printf() число заключенное между двумя
символами ‘%’ ?
1.24. Что такое комментарий, где и как можно его записать в программе?
1.25. Как и для чего используется функция clrscr() ?
1.26. Как и для чего используется функция exit() ?
1.27. Что представляет собой имя массива?
1.28. Каково наименьшее значение индексов массива?
1.29. Каково наибольшее значение индексов массива?
1.30. Что такое идентификатор и какова его наибольшая длина?
1.31. Какой размер памяти занимает значение типа: char, int, float,
double, long double ?
1.32. Опишите процедуру редактирования программы в Borland C++
(включая использование клавиш Home, End, ^y и команд Edit-Copy,
Edit-Cut, Edit-Paste ).
1.33. Рассмотрите вопросы теста тест_01 и выберите правильные ответы
на каждый вопрос.

15
Лабораторная работа N 2

Тема: Операции, операнды, выражения

Цель работы: Правильное использование операций, правильное


составление выражений с соблюдением правил неявного и вынужденного
преобразования данных.

Указания:

Прототипы математических функций, например, pow( x, y ), poly( x,


n, c[] ), sin( x ) и др. находятся в файле <math.h>.

Функция pow( x, y ) вычисляет xy и имеет прототип double pow( double


x, double y );

Функция poly( x, n, c[] ) вычисляет значение многочлена p=cnxn+cn-1xn-1+


…+c2x2+c1x+c0 и имеет прототип double poly( double x, int n, double c[] );

Функция sin( x ) вычисляет значение синуса угла х, выраженного в


радианах и имеет прототип double sin( double x ); Аргументы всех
тригонометрических функций должны быть выражены в радианах. Для
преобразования градусов в радианы, количество градусов умножается на π/
180, где константа π≈3,14159265358979.

Геометрическое среднее чисел a1, a2, …, an равно (a1⋅a2⋅…⋅an)1/n.

Выражение - это один или более операндов связанных между собой


знаками операций. Операция - это то действие, которое нужно выполнить
над операндами. Операнд это константа, имя пременной, вызов функции и
т.д. При вычислении значения выражения соблюдается старшинство
операций, их ассоциативность, а также правило неявных преобразований.
Последовательность выполнения операций можно менять с помощью круглых
скобок, у которых самый высокий проритет. Правило неявных
преобразований следующее: сначала все операнды типа char, short int и
enum из выражения преобразовываются к типу int; далее, если операция
применяется к двум операндам одинакого типа, то результат операции будет
иметь тот же тип что и операнды. Если же операция применяется к двум
операндам разного типа, то операнд “меньшего” типа преобразовывается к
типу второго операнда и только после этого выполняется операция. Результат
будет иметь тип ”старшего” операнда. ”Старшинство” типов в порядке
убывания следующее: long double, double, float, unsigned long, long, unsigned
int, int.
Явные (вынужденные) преобразования, выражения cast. Для того,
чтобы явно преобразовать некоторый операнд к нужному типу, используется
конструкция (tip)operand . Например, (int)x преобразовывает операнд x к
типу int.
16
Операции сравнения: < - меньше, <= - меньше или равно, > -
больше, >= - больше или равно.

Операции равенства: == - равно, != - не равно.

Логические операции: ! - логическое отрицание, && - логическое


”и”, || - логическое ”или“ . Они определяются следующим образом: !а
принимает значение 0 ( false ), если а отлично от нуля, и значение 1 (
true ), если а равно нулю; a&&b принимает значение 1 тогда и только
тогда, когда а и b отличны от нуля; а||b принимает значение 0 тогда и
только тогда, когда и а и b равны нулю.
Результатом операций сравнения, операций равенства и логических
операций является значение 0 ( false ) или 1 ( true ). Он имеет тип int.

Побитовые операции: ~ - дополнение по отношению к 1, << - сдвиг


влево, >> - сдвиг вправо, & - логическое произведение, ^ - сумма по
модулю 2, | - логическая сумма. Эти операции производятся над
операндами типа int.

Примеры программ:

Пример 2.1. Программа читает два числа и выводит их в возрастающем


порядке.

#include <conio.h>
#include <stdio.h>
void main(void)
{ float a,b;
clrscr();
scanf(“%f %f”,&a,&b);
printf(“Numerele citite: %f\t%f\n”,a,b);
printf(“Numerele ordonate: %f\t%f”,
a>b?b:a,a>b?a:b);
getch();
}

Пример 2.2. Программа читает три цифры и определяет, если число,


образованное этими цифрами делится на 3.

#include <conio.h>
#include <stdio.h>
void main(void)
{ unsigned a,b,c;
scanf(“%u %u %u”,&a,&b,&c);
(a+b+c)%3==0 ? printf(“Da”) : printf(“Nu”);
getch();
}

17
Пример 2.3. Программа определяет значения выражений !a&&b, !c||d, !
e&&f||!f&&e.

#include <conio.h>
#include <stdio.h>
void main(void)
{ int a,b; float c,d; double e,f;
clrscr();
scanf(“%d %d”,&a,&b);
printf(“\na=%-d\tb=%-d\t!a&&b=%-d\n\n”,
a,b, !a&&b);
scanf(“%f %f”,&c,&d);
printf(“\nc=%-f\td=%-f\t!c||d=%-d\n\n”,
c,d, !c||d);
scanf(“%lf %lf”,&e,&f);
printf(“\ne=%-lf\tf=%-lf\t!e&&f||!f&&e=%-d”,
e,f, !e&&f||!f&&e);
getch();
}

Пример 2.4. Программа определяет n=max(a,b,c), вычислив d=max(a,b),


и затем n=max(d,c) для трех заданных чисел a, b, c. Предложенная ниже
программа дает неправильный, даже непредсказуемый результат. Почему?!
Исправьте программу для получения правильного результата.

#include <conio.h>
#include <stdio.h>
void main(void)
{ unsigned a=5,b=2,c=3,d,n;
clrscr();
printf(“a=%-u\tb=%-u\tc=%-u\t
max(a,b)=%-u\tmax(a,b,c)= %u”,
a,b,c,d=a>b?a:b,n=d>c?d:c);
getch();
}

Пример 2.5. Программа читает два целых числа и проверяет операции


<< u >>. Проверьте программу для x=1, u=4; x=2, u=8; x=3, u=5; x=216,
u=214; x= -1, u= -1; x= -2, u= -2; x= -4, u= -4; x= -5, u= -5. Объясните
результаты.

#include <conio.h>
#include <stdio.h>
void main(void)
{ long x,y; int u,w;
clrscr();
printf(“Introdu x şi u ”);
scanf(“%ld %d”,&x,&u);
w=u<<2; printf(“u=%-d\t%-d<<2=%-d\n”,u,u,w);
y=x>>2; printf(“x=%-ld\t%-ld>>2=%-ld\n”,x,x,y);
18
getch();
}

Пример 2.6. Программа читает целое число s и находит наименьшее


нечетное число, которое не меньше s.

#include <conio.h>
#include <stdio.h>
void main(void)
{ int s;
clrscr();
scanf(“%d”,&s);
printf(“s=%-d\ts|1=%-d”,s,s|1);
getch();
}

Содержание работы:

Cоставьте программы для нижеследующих примеров. Запишите


программы на сервер и в тетрадь.

2.0. Введите и выполните программы примеров 2.1-2.6.

2.1. Программа читает число х типа float, вычисляет y=3.5x3-3.5x2+3.5x-


3.5 и выводит значения х и у.

2.2. Программа читает число х типа int, вычисляет р=3х2+7х+17 и


выводит значения х и р.
Попробуйте для х=1, 10, 100, 1000. Объясните, почему некоторые
результаты не верны. Измените программу так, чтобы все результаты были
верны.

2.3. Программа читает число х типа double, вычисляет


19 15 9 5
a=x +x +x +x +1 и выводит x и а.
Используйте функцию pow( x, y ) для вычисления ху. Попробуйте для
х=1, 10, 100, 1000. Объясните, почему некоторые результаты не верны. Как
изменить программу так, чтобы все результаты были верны?

2.4. Программа читает аргумент х и коэффициенты c0, c1, c2, c3, c4 типа
double многочлена q=c4x4+c3x3+c2x2+c1x+c0 , вычисляет значение многочлена
q и выводит значения x и q.
Используйте функцию poly( x, n, c[] ) и массив коэффициентов
многочлена для вычисления его значения.

2.5. Программа читает с эхо прописную (заглавную) букву и выводит ее


как заглавную (прописную).
Что произойдет если ввести другой символ ( не букву )?

2.6. Программа читает площадь участка земли, выраженную в “jugăre”,


переводит эту площадь в гектары , ”prăjini pătrate” и “stînjeni pătraţi” ( 1
19
jugăr=576 prăjini pătrate=5764.6415 кв.м., 1 га=10000 кв.м. ) и выводит
результаты.

2.7. Программа читает два целых числа и выводит эти числа и их среднее
геометрическое с двумя дробными знаками.

2.8. Программа читает значение угла, выраженное в градусах, минутах и


секундах тремя целыми числами g, m, s и выводит его значение в радианах
и значение синуса соответствующего угла.
Используйте функцию sin( x ).

2.9. Программа читает число n тuna int и число r тuna double и


выводит 0, если r>n, и –1 в противном случае.

2.10. Программа читает число w и выводит 2000, если 1000<=w<=2000,


и 1000 в противном случае.

2.11. Программа читает целое число из отрезка [1600;4900], являющееся


календарным годом, и выводит 1, если год високосный, и 0, в противном
случае. Год високосный, если он делится без остатка на 4 и не делится на 100
или если делится без остатка на 400.

2.12. Программа читает целое число из отрезка [1600;4900], означающее


календарный год, и выводит количество дней в соответствующем году.

2.13. Программа читает число x, вычисляет 5x2+4.5x , 5x2+4.5x+3.5 и


(5x2+4.5x –1.5)/(5x2+4.5x +1.5) и выводит значения x и соответствующих
выражений.

2.14. Программа читает число х, вычисляет х15, х25, х35, х45 и выводит
результаты.

2.15. Программа читает число х, выводит х, [х] - целую часть, {х} -


дробную часть и значения выражений 8х2-2х+4, 8[х]2-2[х]+4, 8{x}2-2{х}+4.

2.16. Программа читает целое число и выводит его значение и значение


квадратного корня этого числа.
Используйте функцию sqrt( х ).

2.17. Программа читает целое число и выводит значение дроби (n-1)/(n+1)


с 13-тью десятичными знаками.

2.18. Программа читает два числа и выводит наибольшее из них.


Используйте условное выражение.

2.19. Программа читает два числа и выводит их наибольший модуль.


Используйте вложенное условное выражение.

20
2.20. Программа читает число х и выводит z=4х2+9х-2000, если х<0,
z=2000, если х=0 и z=4х2-2000, если х>0.
Используйте одно условное выражение.

Контрольные вопросы:

2.1. Что такое выражение, операнд, операция; какие операции вы знаете?


2.2. Что такое логическое выражение? Какие значения принимает
логическое выражение и какого они типа?
2.3. Объясните правило неявных преобразований.
2.4. Объясните операцию явного преобразования.
2.5. Как используется операция размера sizeof ?
2.6. Как используется операция адреса & ?
2.7. Как используются условные операции ? и : ?
2.8. Какие спецификаторы формата используются для чтения и вывода
данных типа char и типа последовательность символов ?
2.9. Какие спецификаторы формата используются для чтения и вывода
данных целого типа?
2.10. Какие спецификаторы формата используются для чтения и вывода
числовых данных с плавающей запятой одинарной точности ( float )?
2.11. Какие спецификаторы формата используются для чтения и вывода
данных с плавающей запятой и двойной точностью ( double )?
2.12. Какие спецификаторы формата используются для чтения и вывода
данных с плавающей запятой и расширенной точности ( long double )?
2.13. Какого типа являются константы: ‘c’, 3, 3.14, 3.14l, ”3.14” ?
2.14. Что представляет собой прототип функции, где и как он
используется?
2.15. В каком файле находятся прототипы функций pow( x, y ), poly( x, n,
c[] ), cos( x ), sqrt( x ) ?

2.16. В каком диапозоне находятся значения данных типов: int, long,


unsigned, long unsigned ?
2.17. В каком диапозоне находятся значения данных типов: float, double,
long double ?
2.18. В каком диапозоне находятся значения данных типа char ?
2.19. Как оценивается значение выражения ( Pi<2 )+3.4 и какого типа это
значение, если Pi типа float?
2.20. Пусть имеется char c; c=’R’+’b’-‘B’; Каков тип значения с и что
будет выводить printf(“%d”,c); ?
2.21. Пусть имеется int x=2,y; y=x++; x=++y; Каково значение x ?
2.22. Пусть имеется int x=2,y; y=--x; x=y--; Каково значение x ?
2.23. Пусть имеется const int x=3.14, y=2.72; x=y; Каковы значения x и
y и какого они типа?
2.24. Пусть имеется const x=2.72, y=3.14; x=y; Каковы значения x и y
и какого они типа?
2.25. Пусть имеется int x=3.14; y=2.72; x=y; Каковы значения x и у и
какого они типа?
2.26. Пусть имеется int x=2.72; y=3.14; x=y; Каковы значения x и у и
какого они типа?
21
2.27. Пусть имеется int x=1; x=5, x=4, x=3, x=2; Каково значение х ?
2.28. Пусть имеется int n=1; m=2; t=3; z=4; t=n/m; z=n%m; Каковы
значения t u z ?
2.29. Объясните как действуют функции аbs(), ceil(), floor(). Используйте
help.
2.30. Составьте таблицу с приоритетами всех операций ( (), [], ., ->, +
(унарный), - (унарный), ++, --, (type), sizeof(), ~, * (бинарный), /, %,
+ (бинарный), - (бинарный), <<, >>, <, <=, >=, >, ==, !=, & (бинарный),
^, |, &&, ||, ?, :, =, <<=, >>=, +=, -=, *=, /=, %=, &=, ^=, |=, , ).
2.31. Рассмотрите вопросы теста тест_02 и выберите правильные ответы
на каждый вопрос.

22
Лабораторная работа N 3

Тема: Операторы

Цель работы: Изучение операторов языка

Указания:

Составным оператором называется блок операторов следующего


формата: { объявления; операторы; }. В начале блока могут быть
объявлены и инициализированы данные различных типов. С помощью
операторов осуществляется обработка данных.

Оператор if является оператором разветвления. Он имеет два формата:


неполный и полный. Неполный формат имеет вид: if( expr ) instruction1;
Оператор реализует следующую структуру ветвления:

true false
expr

instruction1

Когда встречается такой оператор, вычисляется значение выражения в


скобках. Если выражение имеет значение true, т.е. отличное от 0 значение,
то выполняется instruction1 и затем осуществляется переход к оператору,
следующему непосредственно за оператором if. В противном случае
переходят сразу к следующему оператору.
Полный формат оператора if имеет вид: if( expr ) instruction1; else
instruction2; . В этом случае вычисляется значение выражения в скобках и,
в случае истины, выполняется instruction1, в противном случае –
instruction2. После этого переходят к оператору, следующему за if.
Операторы instruction1 и instruction2 могут быть как простыми, так и
составными. Оператор if в полной форме реализует следующую структуру
ветвления:

23
true false
expr

instruction1

Операторы if могут быть вложены друг в друга.

Оператор for является оператором цикла с предусловием и имеет


формат: for( e1;e2;e3 ) оператор; Здесь е1, е2, е3 являются
выражениями: е1 - выражение инициализации цикла; e2 - выражение,
определяющее условие повтора цикла; e3 - выражение реинициализации
цикла. Все выражения позиционные и могут отсутствовать. оператор
является телом цикла и может быть простым или составным оператором. Тело
цикла – это те операторы обработки данных, которые должны выполняться
несколько раз. Тело цикла может отсутствовать. В этом случае цикл состоит
из заголовка и пустого оператора: for( e1; e2; e3 );
Оператор for выполняется следующим образом: выполняются операции
инициализации цикла е1, затем вычисляется значение выражения е2.
Если е2 имеет отличное от 0 значение, т.е. истинно, то выполняется
оператор - тело цикла. В противном случае, т.е. когда значение е2 равно 0
( ложно ), выполнение цикла for заканчивается и осуществляется переход к
оператору следующему за циклом. После выполнения тела, цикл
реинициализируется - выполняются операции, предусмотренные в е3, и
снова проверяется условие повтора цикла е2.
Оператор for реализует следующую циклическую структуру:

e1

false
e2
true
тело
цикла

e3

24
Оператор while является оператором цикла с предусловием и имеет
формат: while( e1 ) оператор; заголовок цикла - while( e1 ), содержит в
скобках выражение е1, определяющее условие повтора цикла. Тело цикла -
оператор, который может быть простым или составным оператором. Оно
содержит те операции, которые должны быть повторены в цикле.
Этот оператор реализует следующую циклическую структуру:

false
e1

true

тело
цикла

Цикл выполняется следующим образом. Вычисляется значение е1 и тело


цикла выполняется до тех пор, пока е1 принимает значение true. Как
только e1 примет значение false, осуществляется переход к оператору
следующему за циклом.
Тело цикла может отсутствовать. В этом случае цикл состоит из заголовка
и пустого оператора: while( e1 );

Цикл do-while является циклом с постусловием и имеет формат: do


оператор while( e1 ); здесь оператор является телом цикла и содержит те
операторы, которые должны быть повторены в цикле. Он реализует
следующую циклическую структуру:

тело
цикла

e1
true

false

Оператор do-while выполняется следующим образом: сначала


выполняется оператор, т.е. тело цикла, затем вычисляется значение е1 -
условие повтора цикла. Если е1 истинно, то повторяется выполнение тела
цикла. В противном случае, т.е. если е1=0, заканчивается выполнение цикла
и осуществляется переход к оператору, следующему за циклом.
Тело цикла может отсутствовать. В этом случае цикл принимает форму:
do while( e1 );
25
Оператор continue используется только в теле цикла и имеет формат
continue; этот оператор прекращает выполнение текущей итерации цикла и
осуществляет переход к его следующей итерации.

Оператор break используется только в теле цикла или в операторе


switch. При встрече оператора break в теле цикла, выполнение цикла
заканчивается и осуществляется переход к оператору, следующему за
циклом. При встрече оператора break в операторе switch переходят к
оператору, следующему за switch.

Оператор switch является оператором, реализующим одну из нескольких


возможных альтернатив.
Оператор switch имеет следующий формат:

switch( expresie )
{ case c1: şir1; break;
case c2: şir2; break;

case cn: şirn; break;
default: şir; }

Здесь с1, с2, …, сn являются константами того же типа, что и


выражение expr в скобках и определяют альтернативы оператора switch, а
s1, s2,…, sn и s – последовательности операторов. Оператор switch
выполняется следующим образом: определяется значение выражения expr
из скобок, которое сравнивается поочередно со значениями констант с1, с2,
…, сn. При первом совпадении значений выполняется соответствующая
альтернатива case, т.е. соответствующая последовательность операторов.
Например, если первое совпадение имеет место с константой с3, то
выполняется альтернатива case c3, т.е. последовательность операторов s3.
После этого выполняется оператор break, следующий за s3. Этот break
завершает выполнение switch, т.е. осуществляется переход к оператору,
следующему за switch. Если значение выражения из скобок не совпадает ни
с одной константой с1, с2, …, сn, тогда выполняется альтернатива default,
т.е. последовательность операторов s следующая за default. Оператор
default может отсутствовать. Тогда, если значение выражения из скобок не
совпадает ни с одной константой с1, с2, …, сn, оператор switch не имеет
никакого эффекта и осуществляется переход к оператору, следующему за
switch. Оператор default может быть расположен в любом месте между
альтернативами оператора switch, но в таком случае может потребоваться
наличие оператора break после последовательности операторов s.
Операторы break необязательны. В этом случае, после выполнения одной
альтернативы, выполняется следующая и т. д. до встречи оператора break,
которым завершается выполнение оператора switch. Если для различных
констант требуется выполнить одну и ту же последовательность операторов,
то альтернативы могут быть совмещены. Например, если требуется
выполнить ту же последовательность операторов s127 для значений с1, с2
и с7 выражения expr из скобок, то можно записать case c1: case c2: case
c7: s127; break;
26
Операторы case и default используются только в операторе switch
( смотри оператор switch ).

Оператор goto является оператором безусловного перехода. Он имеет


формат goto имя; где имя – имя метки. Метка представляет собой
идентификатор ( имя ), который записывается перед оператором с символом
':' после имени метки. Например, lab1: i++; здесь lab1 - метка. При встрече
оператора goto lab1; переходят сразу к выполнению оператора с меткой
lab1, т.е. к оператору i ++;

Оператор return используется для возврата из функции. Он имеет


формат return; или return expr; Первый формат используется тогда, когда
функция не возвращает никакого значения. Второй формат используется
тогда, когда функция возвращает какое-либо значение, а именно, функция
возвращает значение выражения expr. Тип значения выражения expr
должен совпадать с указанным в заголовке соответствующей функции типом
для возвращаемого значения. Оператор return может быть расположен в
любом месте тела функции, но не обязателен. В теле одной и той же функции
могут быть несколько операторов return.

Примеры программ:

Пример 3.1. Функция находит наибольший общий делитель двух целых


чисел без знака.

unsigned dc(unsigned a, unsigned b)


{ unsigned c,d=1;
if (a==0||b==0) return a>b?a:b;
else for(c=2; c<=(a<b?a:b); c++)
if (a%c==0 && b%c==0) d=c;
return d;
}

Пример 3.2. Программа читает два целых числа без знака и находит их
наименьшее общее кратное. Программа использует функцию предыдущего
примера, сохраненную в файле “f:\bc\mydir\ex3_1.c”.

#include <conio.h>
#include <stdio.h>
#include “f:\bc\mydir\ex3_1.c”
void main(void)
{ unsigned a,b,c;
clrscr();
printf(“Introdu 2 numere întregi fără semn: ”);
scanf(“%u %u”,&a,&b);
c=dc(a,b);
printf(“cmmmc(a,b)=%u”,a*b/c);
getch();
27
}

Пример 3.3. Программа читает цифру и определяет четность числа,


образованного из одной этой цифры.

#include <conio.h>
#include <stdio.h>
void main(void)
{ int n;
clrscr();
printf("Introduceţi o cifră: ");
while(3.14)
{ if (scanf("%d",&n)==1) break;
clrscr();
printf("\nN-aţi introdus o cifră!\n");
printf("Introduceţi o cifră: ");
fflush(stdin); }

printf("\n\nAţi introdus numărul ");


switch(n)
{ case 0: case 2: case 4: case 6:
case 8: printf("par %u",n); break;
default: printf(" %d",n); break;
case 1: case 3: case 5: case 7:
case 9: printf("impar %u",n); }

getch();
}

Пример 3.4. Программа читает последовательность чисел и определяет,


если эти числа образуют арифметическую прогрессию.

#include <conio.h>
#include <stdio.h>
void main(void)
{ int n[100],d=1,i=0,j;
clrscr();

printf("Introduceţi un şir numeric: ");


while( scanf("%d",&n[i])==1 ) i++;

for (j=--i; j>1; j--,i--)


{ if ( (n[i]-n[i-1])!=(n[i-1]-n[i-2]) ) d=0;}

printf("\n\nŞirul introdus ");


if(!d) printf("nu ");
printf("formează o progresie aritmetică.");

getch();
}
28
}

Пример 3.5. Программа читает последовательность чисел и


упорядочивает их в возрастающем порядке.

#include <conio.h>
#include <stdio.h>
void main(void)
{ double t,x[100];int i=0,j=0,k;
clrscr();
while(scanf(“%lf”,&x[i++])==1);
i-=2;
printf(“\nAţi introdus şirul:\n”);
for( ; j<=i; j++) printf(“%lf\t”,x[j]);
for(j=0; j<i; j++)
{ for(k=j+1; k<=i; k++)
{ if( x[k]<x[j] ) { t=x[k],x[k]=x[j],x[j]=t;
}
}}
printf(“\n\nŞirul ordonat este:\n”);
for(j=0; j<=i; j++) printf(“%lf\t”,x[j]);
getch();
}

Пример 3.6. Программа читает слово и представляет его буквы в


обратном порядке. Например, читает слово abc и выводит слово cba или
читает abbc и выводит cbba.

#include <conio.h>
#include <stdio.h>
#include <string.h>
void main(void)
{ char cuv[256],l,i,t;
clrscr();
gets(cuv);
printf(“Aţi introdus cuvântul: %s\n\n”,cuv);
l=strlen(cuv); //lungimea cuvântului cuv
for(i=0; i<l/2; i++)
{ t=cuv[i]; cuv[i]=cuv[l-1-i]; cuv[l-1-i]=t; }
printf(“Cuvântul inversat este: ”);puts(cuv);
getch();
}

Содержание работы:

Составьте программы для нижеследующих заданий. Сохраните программы


на сервере и запишите в тетрадь.

3.0. Наберите и запустите программы примеров 3.1-3.6.


29
3.1. Программа читает х типа float и выводит значение y=5x2+3x-1,
если x≥0 и y=3x-1, если х<0.
Используйте оператор if.

3.2. Программа читает x типа double и выводит y=4x5+5x4-2x+7, если


x<0, y=2000, если x=0 и y=5x4-2x+7, если x>0.
Используйте оператор if.

3.3. Программа читает значения а, b типа long double, решает


уравнение ax+b=0 и выводит результаты.
Исследуйте случаи a≠0; a=0 и b≠0; а=0 и b=0.
Используйте операторы if и scanf() для проверки правильности ввода
данных.

3.4. Программа читает значение a, b, c, d, e, f типа int, решает систему


уравнений по правилу Крамера: x=dx/det, y=dy/det, где det=ac-bd, dx=ce-
bf, dy=af-cd.
Исследуйте все возможные случаи.

3.5. Программа читает значения а, в, с типа long, решает уравнение


2
ах +вх+с=0 и выводит результат.
Исследуйте все возможные случаи для квадратного уравнения, используя в
программе лишь один вложенный оператор if.

3.6. Программа вычисляет и выводит значения многочлена р=3.3х2+7.7х-


10 для x=1, 2, …, 10.
Используйте оператор while.

3.7. Программа вычисляет значения функции sin( x ) для значений х из


отрезка [ 00;3600 ], начиная с х=0 с шагом 1.
Используйте оператор while.

3.8. Программа вычисляет значения функции sin( x ) для х


0 0
принадлежащего отрезку [ 0 ;360 ], начиная с х=0 с шагом 1. Выводит на
экран по 23 значения, останавливаясь каждый раз и ожидая нажатия
какой-нибудь клавиши.
Используйте оператор while.

3.9. Программа читает последовательность целых чисел, разделенных


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

3.10. Программа читает целое n без знака, вычисляет и выводит n! ( n


факториал ) при 0<=n<=170. По определению, n!=1, если n=0 или n=1 и
n!=1⋅2⋅3⋅...⋅n, если n>1.
Используйте операторы if и while.

30
3.11. Программа читает векторы х, у и выводит их скалярное
произведение.
Используйте оператар while и простые переменные ( без индексов ).

3.12. Программа выводит в столбик коды ( printf() ) и соответствующие


символы ( putch() ) расширенного кода ASCII.
Используйте оператор while.

3.13. Программа читает числовой вектор х, вычисляет и выводит для его


координат: 1) среднее арифметическое m=(x1+x2+…xn)/n, 2) среднее
геометрическое (x1*x2*…xn) , 3) квадратическое отклонение (((x1-m)2+(x2-
1/n

m)2+…+(xn-m)2 )/n)1/2.
Используйте операторы while и if и одномерный массив для вектора х.

3.14. Программа читает слово, т.е. последовательность отличных от


пробела символов и выводит все отличные от него преффиксы и суффиксы.
Длина слова не превышает 70 символов. Например, преффиксами слова
abac являются a, ab, aba, а суффиксами являются bac, ac, c.

3.15. Программа читает число а из отрезка [ 0;1 ], вычисляет и выводит


квадратный корень из а с погрешностью меньше чем ε=10-10.
Используйте итерационный метод Ньютона: xn+1=0.5(xn+a/xn), n=0, 1, 2, …
; x0=1. Процесс вычислений прерывается при |xn+1-xn|<ε.

3.16. Программа читает положительное число s, имеющее не более двух


цифр после запятой и представляющее денежную величину. Определяет и
выводит минимальное количество банкнот ( лей ) и монет ( бань )
необходимых для выражения суммы s.
Используйте оператор do-while, тип double и выражения cast.

3.17. Программа читает число типа long unsigned и определяет, является


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

3.18. Программа читает три числа а, в, с, и выводит площадь


треугольника со сторонами a, b, c, если такой существует.
В любом треугольнике сумма длин двух сторон больше длины третьей
стороны.

3.19. Программа читает два числа типа unsigned и выводит их


наименьшее общее кратное.

3.20. Программа читает целое число из отрезка [ 1;7 ] и выводит название


дня недели, соответствующего этому числу ( 1 - понедельник, 2 - вторник,
…, 7 – воскресенье ).
Используйте оператор switch.

31
3.21. Программа читает конструкции “op1 op op2”, где ор1 и ор2 -
числа, а ор - символ, представляющий собой арифметическую операцию (
+, -, * или / ) и выводит значение соответствующего выражения.
Используйте оператор switch.

3.22. Напишите функцию с двумя параметрами х и n, вычисляющую и


возвращающую значение хn, без использования функции pow( x, n ). Здесь
n - типа int, а x - типа long double.

3.23. Программа вычисляет и выводит значения функции xs для s типа


int в отрезке [ m; n ] с шагом 1 и х типа long double.
Включите в программу с помощью директивы #include функцию
предыдущего задания.

3.24. Программа читает последовательность чисел и определяет, если она


является геометрической прогрессией, упорядочив вначале числа в
возрастающем или убывающем порядке.

3.25. Программа читает последовательность чисел и определяет,


упорядочена ли эта последовательность в возрастающем или убывающем
порядке.

Контрольные вопросы:

3.1. Что такое оператор?


3.2. Где и как используется пустой оператор?
3.3. Что представляет собой составной оператор? Приведите примеры.
3.4. Каковы форматы оператора if? Объясните как они работают,
приведите примеры вложенного оператора if.
3.5. Что такое цикл с предусловием? Какие операторы реализуют такой
цикл?
3.6. Что такое цикл с постусловием? Какие операторы реализуют такой
цикл?
3.7. Каков формат цикла for, какой у него заголовок и что представляет
собой тело цикла? Объясните как работает цикл for?
3.8. Каков формат цикла while, какой у него заголовок и что
представляет собой тело цикла? Объясните как работает цикл while.
3.9. Каков формат цикла do-while, что представляет собой тело цикла?
Объясните как работает цикл do-while.
3.10. Каково различие между циклами for, while, do-while ?
3.11. Каков формат оператора switch ? Объясните как работает этот
оператор?
3.12. Где и как используются операторы continue и break ?
3.13. Где и как используются операторы case и default ?
3.14. Где можно поместить объявление в программе?
3.15. Сколько раз будут выполняться циклы: for(;;); for(;;) continue; for(;;)
break; ? Объясните почему.
3.16. Сколько раз будут выполняться циклы: while(3.14); while(0); while(-
1) continue; while(‘0’) continue; while(‘1’) break; ? Объясните почему.
32
3.17. Сколько раз будут выполняться циклы: do { ; } while( 3.14 ); do
{ continue; } while( -1 ); do { break; } while( ‘0’ ); do { continue; } while( ‘0’ );
do { break; } while( ‘1’ ); ? Объясните почему.
3.18. Пусть имеется char h; h=getch(); puts(“Ввели ”);
switch( h )
{ default: puts(“неправильно”);
case ‘1’: printf(“%d\n”,1);
case ‘2’: printf(“%d\n”,2);
case ‘3’: printf(“%d\n”,3);
case ‘4’: printf(“%d\n”,4);
}
Что будет выведено на экране? Объясните почему.
3.19. Пусть имеется int x; do { clrscr(); puts(“Введите целое число! ”) }
while( !scanf(“%d”,&x) ); Сколько раз будет выполняться цикл? Объясните
почему.
3.20. Пусть имеется float x,y,z; do { clrscr(); puts(“Введите три числа! ”) }
while( !(scanf(“%f%f%f”,&x,&y,&z)==3) ); Сколько раз будет выполняться
цикл? Объясните почему.
3.21. Пусть имеется int a,b; printf(“Введите целые a и b: ”); for( ;
scanf(“%d%d”,&a,&b)!=2; ) { clrscr(); printf(“Неправильный ввод.
Повторите!\n”); printf(“Введите целые a и b: ”); }. Сколько раз будет
выполняться цикл? Объясните почему.
3.22. Пусть имеется long double w; printf(“\nw=”); while( scanf(“%Lf”,&w)!
=1 ) { clrscr(); printf(“Неправильно! Повторите ввод.\n”); printf(“\nw=”); }.
Сколько раз будет выполняться цикл? Объясните почему.
3.23. Где и как используется оператор return ? Приведите примеры.
3.24. Рассмотрите вопросы теста тест_03 и выберите правильные ответы
на каждый вопрос.

33
Лабораторная работа N 4

Тема: Указатели ( поинтеры )

Цель работы: Использование указателей ( поинтеров ) при обработке


данных.

Указания:

Указатель ( поинтер ) – это переменная, значениями которой являются


адреса памяти. По адресам-значениям указателя могут храниться данные
определенного типа. Соответствующий тип данных указывается в
объявлении типа указателя и называется основным ( базовым ) типом
указателя или типом, на который указывает указатель.

Объявление указателя имеет формат tip *name, где name - имя


указателя, который объявляется, а tip – его базовый тип, т.е. тип данных,
которые будут храниться по адресам-значениям этого указателя. Например:
int x; int *p; Здесь объявлены две переменные: х и p; x - простая
переменная типа int, а р - указатель на тип int, т.е. значениями
переменной p будут адреса памяти и по этим адресам будут храниться
данные типа int. Таким образом, int * является типом данных. Этот тип
называется указатель на тип int. Он указывает ( поинтирует ) на тип int.
Аналогично char *, float *, double *, long *, int **, long double *** и т.д.
являются типами указателя на char, float, double, long, int *, long double **.
Можно объявить и массивы указателей: char *s[234]; unsigned *r[25][25]; и
т.д.

Выделение памяти для указателей. Указателю выделяется, как


правило, машинное слово, т.е. 2 байта. Однако, модификаторы near, far,
huge, примененные к типу указатель, меняют способ выделения памяти для
указателей. Модификаторы far и huge выделяют по 2 слова, т.е. 4 байта
памяти для указателей, а near, или отсутствие модификатора, означает
выделение для указателя одного слова, т.е. 2 байтов памяти.

Присвоение значений указателям производится при помощи операции


определения адреса &: int x=10; int *p; p=&x; Здесь х - простая
переменная, которой присваивается начальное значение 10, р - указатель
на тип int и р присваивается, в качестве значения, адрес переменной х.
При объявлении указателям могут быть присвоенны начальные значения:
float w; float *y=&w; или char *ps=“Cibernetica”; здесь ps - указатель
типа char * и ему присваевается, в качестве значения, адрес начала
последовательности символов “Cibernetica”, т.е. адрес первого символа
последовательности.

Указатели типа void * ( указатели без типа ). Могут быть объявленны


указатели, для которых не идентифицируется базовый тип данных на
34
которые они указывают, например, void *p. В этом случае не известен
заранее тип данных, которые будут храниться по адресам-значениям
указателя. При присвоении значений или чтении данных с использованием
указателя типа void *, необходимо явно указать тип соответствующих
данных.

Доступ к данным по указателю производится при помощи операции *


( обращение к данномому по адресу, а не по имени ). Если р - указатель, то
*р - данное, хранимое по адресу р. Например, пусть имеется int x=10; *p;
и p=&x; тогда р является указателем на int, и его значением является
адрес х, а *р представляет собой значение переменной х, т.е. 10.
Присвоение *р=20; приведет к записи значения 20 по адресу, выделенному
для переменной х, т.е. равносильно присвоению х=20; таким же образом,
если имеется float r, *q; q=&r; *q=3.141593, тогда переменной r
присваивается значение 3.141593.
При использовании указателя типа void * уточняется его базовый тип,
т.е. явно указывается тип данного, которое будет хранится по
соответствующему адресу. Пусть имеется float x=2.82; void *r; r=&x; тогда
выражение *r ошибочно, потому что при объявлении указателя не определен
основной тип. Необходимо писать *(float *)r. Также ошибочно выражение
*r+7.18. Правильно будет *(float*)r+7.18;

Для вывода значения указателя используется буква р в


соответствующем спецификацаторе формата. Например, если имеется int
x=1, *px=&x; double z=3.14, *pz=&z; тогда print(“%p%d%p
%lf”,px,*px,pz,*pz); выведет на экран адреса и значения х и z,
соответственно. Адрес выводится в шестнадцатиричном виде.

Операции над указателями. Над указателями могут выполняться


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

Операции инкремента ( ++ ) и декремента ( -- ) для указателей.


Операция ++ ( -- ), произведенная над указателем, увеличивает ( уменьшает
) его значение на число байтов, необходимых для хранения данных, на
которые этот указатель указывает. Например, если имеется int *p; тогда +
+р, также как и р++, увеличивает значение р на 2, потому что для
значения типа int выделяются 2 байта. Соответственно, если имеется
double *w; тогда w++, также как и ++w, увеличивает значение w на 8,
поскольку тип double требует 8 байтов.

Прибавление ( вычитание ) целого положительного числа к указателю


( из указателя ) указателя . Пусть р - указатель. Тогда р+n ( р-n )
увеличивает ( уменьшает ) значение указателя р на n*r байтов, где r -
количество байтов, необходимых для данных типа, на который указывает р.
Например, если имеется char *c; float *f; long doble *w ; и unsigned n=5;
тогда c+n, f+n, w+n; увеличивают значения указателей с, f, w на 5*1=5,
5*4=20 и 5*10=50 байтов, соответственно, поскольку для типа char

35
требуется 1 байт, для типа float - 4 байта и для типа long double - 10
байтов памяти, соответственно.

Сравнение указателей используется, как правило, для указателей,


указывающих на элементы одного и того же массива. Пусть р - указатель
на элемент tab[i] массива tab ( т.е. значением р является &tab[i] ), а q -
указатель на элемент tab[j] того же массива tab ( т.е. значением q
является &tab[j] ). Тогда к указателям р и q можно применить операции
сравнения. Так, выражение р<q истинно только в случае, если i<j.
Указатель может быть сравнен с пустым указателем, который имеет значение
0 и не представляет никакого адреса. Для пустого ( нулевого ) указателя в
<stdio.h> определена специальная константа с именем NULL.

Разность двух указателей используют, как правило, для указателей на


элементы одного и того же массива. Пусть р и q указатели на элементы
tab[i] и tab[i+n], соответственно, где n - целое положительное число, тогда
разность q-p представляет собой именно число n. В свою очередь разность
p-q равна значению -n.

Связь между указателями и массивами. Пусть tab[n] - одномерный


массив из n элементов. Тогда tab является указателем со значением,
равным адресу первого элемента массива, т.е. &tab[0]. Tогда tab+1 - адрес
элемента с индексом 1, т.е. &tab[1], tab+2 - адрес элемента с индексом 2,
т.е. &tab[2] и т.д. Соответственно, *(tab+0), или просто *tab, представляет
собой элемент tab[0], *(tab+1) - элемент tab[1], *( tab+2) - элемент tab[2] и
т.д.
В общем случае, если p ненулевой поинтер, а n неотрицательное целое
число, то p[n]=*(p+n) и &p[n]=p+n.
Пусть теперь tab[m][n] - двумерный массив с m строками и n
столбцами. Тогда tab[0] - указатель, значением которого является адрес
начала строки 0 массива tab[m][n], т.е. &tab[0][0], tab[1] - указатель на
начало строки 1 массива tab[m][n], т.е. имеет значение &tab[1][0] и т.д.
Следовательно, tab[0], tab[1], tab[2], ..., tab[m] является массивом
указателей. В свою очередь, имя tab массива tab[m][n] представляет собой
указатель на массив указателей tab[0], tab[1], ..., т.е. значением указателя
tab является адрес указателя tab[0]. Отсюда следует, что выражения
&tab[i][j], tab[i]+j, *tab+i*n+j равносильны. Равнозначны также выражения
tab[i][j], *(tab[i]+j), *(*tab+i*n+j), следовательно, **tab - не что иное, как
елемент tab[0][0].

Примеры программ:

Пример 4.1. Программа определяет наибольший член числовой


последовательности.

#include <conio.h>
#include <stdio.h>
void main()
{ int x[50]={1,2,3,4,5},*px,i,r,t;
36
clrscr();
px=x;
r=*px;
for(i=0;i<5;i++)
{ t=*(px+i);
if( r<t ) r=x[i]; }
printf("Cel mai mare număr din şirul x este
%d",r);
getch();
}

Пример 4.2. Программа переставляет символы последовательности в


обратном порядке, например ASEM → MESA.

#include <conio.h>
#include <stdio.h>
#include <string.h>
void main()
{ char s[256]="abracadabra",i,j,t;
clrscr();
puts("Şirul iniţial:\n");
puts(s);
for(i=0,j=strlen(s)-1; i<j; i++,j--)
{ t=*(s+i); *(s+i)=*(s+j); *(s+j)=t; }
puts("\nŞirul inversat:\n");
puts(s);
getch();
}

Пример 4.3. Программа вычисляет сумму элементов с четным индексом


для заданной числовой последовательности.

#include <conio.h>
#include <stdio.h>
#include <string.h>
void main()
{ unsigned x[30]={20,22,10,25,20},s=0,i=0;
clrscr();
puts("Şirul iniţial este:");
while( s<5 ) printf("%u\t",*(x+s++));
s=0;
while( i<5 )
{ s+=*(x+i); i+=2; }
printf("\nSuma elementelor de indice
par este %u",s);
getch();
}

Пример 4.4. Программа определяет в символьной последовательности


символ с наибольшим кодом.
37
#include <conio.h>
#include <stdio.h>
#include <string.h>
void main()
{ char sir[256]="Asem CSIE",c,i=-1;
clrscr();
c=*sir;
for( ; ++i<strlen(sir) ; )
{ if (c<*(sir+i)) c=*(sir+i); }
puts("Caracterul de cod maxim în şirul");
printf("%s\neste %c",sir,c);
getch();
}

Пример 4.5. Программа упорядочивает в порядке убывания элементы


числовой последовательности ( см. также примеры 4.6, 4.7 ).

#include <conio.h>
#include <stdio.h>
void main()
{ short i,j,t,n=12;
double x[234]={2,5,6,4,2,1,7,8,2,5,4,55};
clrscr();
puts("Şirul iniţial:\n");
for(i=0;i<n;i++) printf("%lf\t",x[i]);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if( x[i]<x[j] ) { t=x[i];x[i]=x[j];x[j]=t; }
puts("\n\nŞirul ordonat descrescător:\n");
for(i=0;i<n;i++) printf("%lf\t",x[i]);
getch();
}

Пример 4.6. Программа упорядочивает элементы числовой


последовательности в убывающем порядке( см.также примеры 4.5, 4.7 ).

#include <conio.h>
#include <stdio.h>
void main()
{ short i,j,t,n=12;
long double x[234]={2,5,6,4,2,1,7,8,2,5,4,55};
clrscr();
puts("Şirul iniţial:\n");
for(i=0;i<n;i++) printf("%.1Lf\t",i[x]);
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
if( x[i]<j[x] ) { t=*(x+i);x[i]=*&x[j];x[j]=t;
}
puts("\n\nŞirul ordonat descrescător:\n");
38
for(i=0;i<n;i++) printf("%.1Lf\t",*&i[x]);
getch();
}

Пример 4.7. Программа упорядочивает последовательность символов в


возрастающем порядке ( см. также примеры 4.5, 4.6 ).

#include <conio.h>
#include <stdio.h>
void main()
{ char x[256]="ACADEMIA DE STUDII ECONOMICE";
int n=27,i,j,t;
clrscr();
puts("Şirul iniţial:\n");
puts(x);
for(i=0;i<n;i++)
for(j=i+1;j<=n;j++)
if( x[i]>x[j] ) { t=x[i];x[i]=x[j];x[j]=t; }
puts("\n\nŞirul ordonat crescător:\n");
printf("*%s*",x);
getch();
}

Пример 4.8. Программа выводит адреса символов и сами символы двух


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

#include <conio.h>
#include <stdio.h>
void main()
{ char c[15]="Cibernetica",s[15]="Statistica";
int i;
clrscr();
for(i=-2;i<13;i++)
printf("%p\t%c\t%p\t%c\n",c+i,c[i],s+i,s[i]);
getch();
}

Пример 4.9. Программа выводит адреса и сами элементы символьной


матрицы и демонстрирует различные способы обращения к элементам
двумерного массива.

#include <conio.h>
#include <stdio.h>
void main()
{ char a[5][5]={ 'a','b','c','d','e',
'f','g','h','i','j',
'k','l','m','n','o',
'p','q','r','s','t',
39
'u','v','w','x','y' };
int i,j;
clrscr();

for(i=0;i<5;i++)
for(j=0;j<5;j++)
{ printf("%p\t%p\t%p\t", &a[i][j],
&*(a[i]+j), &*(*a+5*i+j));
printf("%c\t%c\t%c\t", a[i][j],
*(a[i]+j), *(*a+5*i+j));
printf("%p\t%p\t%p\n", &a[i][j],
(a[i]+j), (*a+5*i+j));
getch(); }

getch();
}

Содержание работы:

Составьте программы для нижеследующих заданий. Используйте в


программах указатели! Запишите программы на сервер и в тетрадь.

4.0. Наберите и выполните программы из примеров 4.1-4.9.

4.1. Программа читает три числа: a типа int, b типа float и c типа
double. Для введенных данных программа вычисляет и выводит: введенные
числа, наибольшее число, наименьшее число, сумму чисел, произведение
чисел, их среднее арифметическое с двумя десятичными знаками после
запятой, их геометрическое среднее с тремя десятичными знаками после
запятой.

4.2. Программа определяет наименьшее число среди элементов с четным


индексом числовой последовательности.

4.3. Программа меняет местами элементы с нечетным индексом и


элементы с четным индексом в последовательности символов ( элемент с
индексом 0 и элемент с индексом 1 меняется местами, элемент с индексом 2
и елемент с индексом 3 меняется местами и т.д. ).

4.4. Программа вычисляет произведение элементов с нечетными


индексами ( 1, 3, … ) числовой последовательности.

4.5. Программа определяет в последовательности символов символ с


наименьшим четным кодом.

4.6. Программа меняет местами в числовой последовательности


наибольший и наименьший элементы.

40
4.7. Программа вычисляет скалярное произведение p двух векторов x, y
( p=x1⋅y1+x2⋅y2+…+xn⋅yn ), не используя массивы.

4.8. Программа вычисляет элементы вектора y=( y1,y2,…,yn ), являющегося


произведением матрицы A размерности mxn с элементами aij, i=1, 2, …,
m, j=1, 2, …, n на вектор x=( x1,x2,…,xm ) ( yi=ai1⋅x1+ai2⋅x2+…+aim⋅xm, i=1, 2,
3, …, n ).

4.9. Программа вычисляет элементы матрицы C размерности mxn с


элементами cij, i=1, 2, …, m, j=1, 2, …, n, являющейся произведением
матриц A размерности mxp с элементами aik, i=1, 2, …, m, k=1, 2, …, p
и B размерности pxn с элементами bkj, k=1, 2, …, p, j=1, 2, …, n (
cij=ai1⋅b1j+ai2⋅b2j+…+aip⋅bpj, i=1, 2, …, m, j=1, 2, …, n ).

4.10. Программа составляет и выводит все слова ( имеющие смысл! ),


которые могут быть составлены из букв слова “TRACTOR” ( RAC, TOR,
ROTOR и т. д. ). Образуйте слова, выводя нужные буквы с помощью
поинтеров.

Контрольные вопросы:

4.1. Что такое указатель?


4.2. Сколько памяти отводится для переменной типа указатель?
4.3. Как выводится на экран значение указателя?
4.4. Какова связь между указателями и операцией определения адреса & ?
4.5. Какова связь между указателями и операцией обращения к данным по
адресу * ?
4.6. Пусть имеется char tab[256]; int vec[33]; float mat[4][3]; double cub[2]
[3][4]; Что представляют собой имена tаb, vec, mat, cub, mat[2], cub[1],
cub[2][3] ?
4.7. Что представляют собой указатели hear, far, huge ? Чем они
отличаются, как размещаются в памяти?
4.8. Что такое пустой указатель, что представляет собой константа NULL
и где она определена?
4.9. Сколько памяти отводится для указателей: int near *px; float far *py;
double huge *pz; char *pa; long huge *pd; long double *pw; ?
4.10. Пусть имеется char a=’a’,*pa; int x =10, *px; float y=2.8, *py; double
z =12345.54321, *pz; long double w =123456789.987654321, *pw; pa =&a;
px=&x; py=&y; pz=&z; pw=&w; Что выведет printf(“%c\t%d\t%f\t%lt\t%Lf\
n”, *pa,*px,*py,*pz,*pw); ?
4.11. Пусть имеется char a, *pa=&a; int x, *px=&x; float q, *pq=&q; double
r, *pr=&r; long doable w , *pw=&w; scanf(“%c%d%f%lf%Lf”,pa,px,pq,pr,pw);
Что нужно вводить с клавиатуры для этого scanf() и что выведет
printf(“%c\t%d\t%f\t%lf\t%Lf\n”,*pa,*px,*pq,*pr,*pw); ?
4.12. Пусть имеется int x=2003, *p=&x, **q=&p, ***w=&q; Что выведет
printf(“%d%d%d%d”,x,*p,**q,***w); ?
4.13. Пусть имеется char s[22]=” ’0’,’1’,’2’,’3’,’4’,’5’ ”, *ps=s; int
x[22]={0,1,2,3,4,5}, *px=x; float y[22]={0,1,2,3,4,5}, *py=y; double

41
z[22]={0,1,2,3,4,5}, *pz=z; long double w[22]={0,1,2,3,4,5}, *pw=w; Что
выведет printf(“%c%d%f%lf%Lf”,*(ps+1),*(px+2),*(py+3),*(pz+4),*(pw+5)); ?
4.14. Пусть имеется short i[22]={0,1,2,3,4,5}, *pi=i; Что выведет
printf(“%d%d%d%d%d%d”,*pi,*pi++,*pi++,*pi++,*pi++,*pi++); ?
4.15. Пусть имеется int i[22]={0,1,2,3,4,5}, *pi=i; Что выведет printf(“%d
%d%d%d%d%d”,*pi,*++pi,*++pi,*pi++,*pi++,*pi++); ?
4.16. Пусть имеется float i[22]={0,1,2,3,4,5},*pi=i+5; Что выведет
printf(“%f%f%f%f%f%f”,*pi,*pi--,*pi--,*pi--,*pi--,*pi--); ?
4.17. Пусть имеется double i[22]={0,1,2,3,4,5), *pi=i+5; Что выведет
printf(“%d%d%d%d%d%d”,*pi,*--pi,*--pi,*--pi,*--pi,*--pi); ?
4.18. Рассмотрите вопросы теста тест_04 и выберите правильные ответы
на каждый вопрос.

42
Лабораторная работа N 5

Тема: Функции обработки символьных последовательностей

Цель работы: Использование указателей и стандартных функций при


обработке символьных последовательностей.

Указания:

В приведенных ниже заданиях подразумевается, что text представляет


собой последовательность, состоящую из не более чем 100 слов. Любое слово
состоит из не более чем 30 символов, отличных от пробела. При упорядочении
текстов меняются местами указатели на слова, а не сами слова.
Календарный год високосный, если он делится без остатка на 4 и не
делится на 100, или, если он делится без остатка на 400.

Существует множество функций для обработки символьных


последовательностей. Как правило, прототипы этих стандартных функций
содержатся в файле <string.h> и их имена начинаются с приставки str.
Функция strcmp() имеет прототип int strcmp( const char *s1, const
char *s2 ); Она сравнивает символьные последовательности с1 и с2, на
которые указывают указатели s1 и s2, соответственно. Функция
возвращает положительное значение, если c1>c2, значение 0, если с1=с2,
и отрицательное значение, если c1<c2.

Функция stricmp() имеет прототип int stricmp( const char *s1, const
char *s2 ); Она сравнивает символьные последовательности с1 и с2, на
которые которые указывают указатели s1 и s2, соответственно, не
различая прописные и заглавные буквы. Функция возвращает положительное
значение, если c1>c2, значение 0, если с1=с2, и отрицательное значение,
если c1<c2.

Функция strncmp() имеет прототип int strncmp( const char *s1, const
char *s2, unsigned n ); Она сравнивает не более n первых символов
последовательностей с1 и с2, на которые указывают указатели s1 и s2,
соответственно. Функция возвращает положительное значение, если c1>c2,
значение 0, если с1=с2, и отрицательное значение, если c1<c2.

Функция strnicmp() имеет прототип int strnicmp( const char *s1, const
char *s2, unsigned n ); Она сравнивает не более n первых символов
последовательностей с1 и с2, на которые указывают указатели s1 и s2,
соответственно, не различая прописные и заглавные буквы. Функция
возвращает положительное значение, если c1>c2, значение 0, если с1=с2,
и отрицательное значение, если c1<c2.

Функция strlen() имеет прототип unsigned strlen( const char *s ); Она


возвращает количество символов последовательности с, на которую
43
указывает указатель s. Символ NUL, находящийся в конце
последовательности, не учитывается.

Функция strcpy() имеет прототип char *strcpy( char *d, const char *s );
Она копирует содержимое последовательности, на которую указывает
указатель s, в последовательность на которую указывает указатель d.
Копируется и символ ‘\0’. Функция возвращает значение указателя d.

Функция strncpy() имеет прототип char *strnpy( char *d, const char *s,
unsigned n ); Она копирует не более n первых символов, содержащихся в
последовательности, на которую указывает указатель s, в
последовательность на которую указывает указатель d. Функция возвращает
значение указателя d.

Функция strcat() имеет прототип char *strcat( char *d, const char *s );
Она сцепляет последовательности, на которые указывают указатели d и s:
копирует содержимое последовательности, на которые указывает указатель
s, в продолжение, после последнего символа последовательности, на которую
указывает указатель d. Копируется и символ ‘\0’. Функция возвращает
значение указателя d.

Функция strncat() имеет прототип char *strncat( char *d, const char *s,
unsigned n ); Она сцепляет к последовательности, на которую указывает
указатель d, не более n первых символов последовательности, на которую
указывает указатель s: копирует не более n первых символов,
содержащихся в последовательности, на которую указывает указатель s, в
продолжение, за последним символом последовательности, на которую
указывает указатель d. Функция возвращает значение указателя d.

Функция strchr() имеет прототип char *strchr( const char *s, int c ); Она
осуществляет поиск первого появления символа c в последовательности, на
которую указывает указатель s.
Функция возвращает указатель на первое появление символа c в
соответствующей последовательности или указатель NULL.

Функция strstr() имеет прототип char *strstr( const char *s1, const char
*s2 ); Она находит первое вхождение последовательности c2, на которую
указывает указатель s2, в последовательности c1, на которую указывает
указатель s1.
Функция возвращает указатель на первое появление последовательности
s2 в последовательности s1 или указатель NULL.

Функция malloc() имеет прототип void *malloc( unsigned n );


содержащийся в файле <alloc.h>. Она выделяет в памяти heap
непрерывную зону из n байтов и возвращает указатель типа void,
являющийся начальным адресом выделенной зоны. Функция возвращает
нулевой указатель, если память не выделена. ( Смотри также функции
calloc(), farmalloc(), farcalloc() ).

44
Функция free() имеет прототип void free( void *p ); содержащийся в
файле <alloc.h>. Она освобождает память heap, на которую указывает
указатель p. ( Смотри также функцию void farfree( void far *p ); ).

Примеры программ:

Пример 5.1. Функция рассматривает текст из трех слов и возвращает


указатель на самое большое слово.

char *bw(char *c1, char *c2, char *c3)


{ char *p;
p=( strcmp(c1,c2)>0 )?c1:c2;
return ( strcmp(p,c3)>0 )?p:c3;
}

Пример 5.2. Программа читает текст из четырех слов и выводит самое


большое слово. В программе используется функция из предыдущего примера,
записанная в файле “f:\bc\mydir\ex5_1.c”.

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include “f:\bc\mydir\ex5_1.c”
void main(void)
{ char c1[30], c2[30], c3[30], c4[30], *q;
clrscr();
printf(“Introdu un text din patru cuvinte: ”);
scanf(“%s %s %s %s”,c1,c2,c3,c4);
printf(“\nAţi introdus textul:\n\n”);
printf(“%s %s %s %s\n\n”,c1,c2,c3,c4);
q=bw(c1,c2,c3);
q=( strcmp(q,c4)>0 )?q:c4;
printf(“Cel mai mare cuvânt este: ”);
puts(q);
getch();
}

Пример 5.3. Функция рассматривает текст из трех слов и возвращает


указатель на самое короткое слово.

char *sw(char *w1, char *w2, char *w3)


{ char *p;
p=( strlen(w1)<strlen(w2) )?w1:w2;
return ( strlen(p)<strlen(w3) )?p:w3;
}

Пример 5.4. Программа читает текст из пяти слов и выводит самое


короткое слово. В программе используется функция предыдущего примера,
которая была записана в файл “f:\bc\mydir\ex5_3.c”.
45
#include <conio.h>
#include <stdio.h>
#include <string.h>
#include “f:\bc\mydir\ex5_3.c”
void main(void)
{ char z1[30], z2[30], z3[30], z4[30], z5[30], *w;
clrscr();
printf(“Introdu un text din cinci cuvinte: ”);
scanf(“%s %s %s %s %s”,z1,z2,z3,z4,z5);
printf(“\nAţi introdus textul:\n\n”);
printf(“%s %s %s %s %s\n\n”,z1,z2,z3,z4,z5);
w=sw(z1,z2,z3);
w=sw(w,z4,z5);
printf(“Cel mai scurt cuvânt este: ”);
puts(w);
getch();
}

Пример 5.5. Функция записывает слово в память heap. Она возвращает


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

char *wh(char *w)


{ char *p;
p=(char *)malloc( strlen(w)+1 );
if( p ) strcpy(p,w);
return p;
}

Пример 5.6. Функция упорядочивает текст в убывающем порядке слов.

void sd(char *wp[], unsigned n)


{ char *t, i=0,j;
while( i<n-1 )
{ j=i+1; t=wp[i];
while( j<n )
{ if( strcmp(t,wp[j])<0 ) { t=wp[j];
wp[j]=wp[i]; wp[i]=t; }
j++; }
i++; }
}

Пример 5.7. Функция упорядочивает текст в порядке возрастания длин


его слов.

void ld(char *wp[], unsigned n)


{ char *t, i=0,j,d,r;
while( i<n-1 )
{ j=i+1; t=wp[i]; d=strlen(wp[i]);
46
while( j<n )
{ r=strlen(wp[j]);
if( r<d ) { d=r; t=wp[j];
wp[j]=wp[i]; wp[i]=t; }
j++; }
i++; }
}

Пример 5.8. Функция имеет в качестве параметра номер месяца и


возвращает указатель на его название. Предполагается, что номер месяца
правильный.

char *dl(unsigned nl)


{ static char *mn[13]=
{“”,”Ianuarie”, “Februarie”, “Martie”,
“Aprilie”, “Mai”, “Iunie”,
“Iulie”, “August”, “Septembrie”,
“Octombrie”, “Noiembrie”, “Decembrie”};
return mn[nl]; }

Пример 5.9. Функция имеет в качестве параметра название месяца и


возвращает его номер. Предполагается, что название месяца правильное.

unsigned nl(char *dl)


{ static char *mn[13]=
{“”,”Ianuarie”, “Februarie”, “Martie”,
“Aprilie”, “Mai”, “Iunie”,
“Iulie”, “August”, “Septembrie”,
“Octombrie”, “Noiembrie”, “Decembrie”};
unsigned n;
for(n=1; n<13; n++)
if( !strcmp(dl,mn[n]) ) return n;
}

Пример 5.10. Функция имеет в качестве параметра календарную дату в


форме z luna a, где z - число, luna - название месяца, и a - год
( четыре цифры ) и возвращает номер дня в году для соответствующей даты.
Предполагается, что календарная дата правильная.

unsigned nz(unsigned z, char luna[], unsigned a)


{ unsigned i=1,nl=0,d=0;
static unsigned zl[]=
{0, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
static char *dl[]=
{“”, “Ianuarie”, “Februarie”, “Martie”,
“Aprilie”, “Mai”, “Iunie”,
“Iulie”, “August”, “Septembrie”,
“Octombrie”, “Noiembrie”, “Decembrie”};
while( strcmp(luna,dl[++nl]) ) ;
47
for( ; i<nl; i++)
d+=zl[i]; d+=z;
if( nl>2 ) d+=( (a%4==0)&&(a%100)||(a%400==0) );
return d;
}

Пример 5.11. Функция определяет календарную дату по году ( четыре


цифры ) и номеру дня в этом году.

void daz(unsigned an, unsigned zi, int *d, char l[])


{ static char *dl[]=
{“”, “Ianuarie”, “Februarie”, “Martie”,
“Aprilie”, “Mai”, “Iunie”,
“Iulie”, “August”, “Septembrie”,
“Octombrie”, “Noiembrie”, “Decembrie”};
static unsigned nz[]=
{0, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
unsigned nl=0,s=0;
nz[2]+=( (an%4==0)&&(an%100)||(an%400==0) );
while( s<zi ) s+=nz[++nl];
s-=nz[nl]; *d=zi-s; strcpy(l,dl[nl]);
}

Пример 5.12. Функция определяет название дня недели по календарной


дате dd luna yyyy. Предполагается, что календарная дата правильная, luna
- название месяца, год yyyy состоит из четырех цифр. Используется
функция nz() из примера 5.10, которая была записана в
“f:\bc\mydir\ex5_10.c”.

char *ziua(unsigned dd, char luna[], unsigned yyyy)


{ unsigned b=0,d1,d2,a1,a2; char m1[30],m2[30];
unsigned nd1,nd2,nd1nd2,i,r,bi;

/* Fie cunoscută denumirea zilei pentru o dată


reper, de exemplu 16 Noiembrie 1999 – Marţi
*/

d1=dd, strcpy(m1,luna), a1=yyyy,


d2=16, strcpy(m2,"Noiembrie") ,a2=1999;
nd1=nz(d1,m1,a1); nd2=nz(d2,m2,a2);

if( yyyy<1999 )
{ if( a2-a1==1 )
{ b=(a1%4==0)&&(a1%100)||(a1%400==0);
nd1nd2=nd2+365+b-nd1; }
else { for(b=0,i=a1;i<a2;i++)
b+=(i%4==0)&&(i%100)||(i%400==0);
bi=(a1%4==0)&&(a1%100)||(a1%400==0);
nd1nd2=365*(a2-a1-1)+b+nd2+
48
365+bi-nd1; }
r=nd1nd2%7; return eliz[r]; }

if( yyyy>1999 )
{ d2=dd, strcpy(m2,luna), a2=yyyy,
d1=16, strcpy(m1,"Noiembrie") ,a1=1999;
nd1=nz(d1,m1,a1); nd2=nz(d2,m2,a2);
if( a2-a1==1 )
{ b=(a1%4==0)&&(a1%100)||(a1%400==0);
nd1nd2=nd2+365+b-nd1; }
else { for(b=0,i=a1;i<a2;i++)
b+=(i%4==0)&&(i%100)||(i%400==0);
bi=(a1%4==0)&&(a1%100)||(a1%400==0);
nd1nd2=365*(a2-a1-1)+b+nd2+
365+bi-nd1; }
r=nd1nd2%7; return zile[r]; }

if( (yyyy==1999)&&(nd1>nd2) )
{ d2=dd; strcpy(m2,luna); a2=yyyy;
d1=16, strcpy(m1,"Noiembrie") ,a1=1999;
nd1=nz(d1,m1,a1); nd2=nz(d2,m2,a2);
printf("nd1=%u nd2=%u\n",nd1,nd2); getch();
nd1nd2=nd2-nd1; r=nd1nd2%7; return zile[r]; }
else { nd1nd2=nd2-nd1; r=nd1nd2%7;
return eliz[r]; }

Пример 5.13. Программа читает календарную дату dd luna yyyy и


определяет название дня недели по прочитанной дате. Здесь dd – число,
luna – название месяца и четырехзначное число yyyy - год
соответствующей даты. Предполагается, что календарная дата вводится
правильно. В программе используются функции из примеров 5.10 и 5.12,
которые записаны в файлах “f:\bc\mydir\ex5_10.c” и
“f:\bc\mydir\ex5_12.c”, соответственно.

#include <conio.h>
#include <stdio.h>
#include <string.h>

unsigned zl[]=
{0,31,28,31,30,31,30,31,31,30,31,30,31};
char *dl[]=
{ "", "Ianuarie", "Februarie", "Martie",
"Aprilie", "Mai", "Iunie",
"Iulie", "August", "Septembrie",
"Octombrie", "Noiembrie", "Decembrie" };
char *zile[]=
{ "Marţi", "Miercuri", "Joi", "Vineri",
"Sâmbătă", "Duminică", "Luni" };
49
char *eliz[]=
{ "Marţi", "Luni", "Duminică", "Sâmbătă",
"Vineri", "Joi", "Miercuri" };

#include "f:\bc\mydir\ex5_10.c"
#include "f:\bc\mydir\ex5_12.c"

void main(void)
{ unsigned d,y; char luna[30], *p;
clrscr();
printf("Introdu o dată calendaristică
dd luna yyyy: ");
scanf("%2d %s %4d",&d,luna,&y);
printf("\nAti introdus data: %2d %s %4d\n\n",
d,luna,y);
p=ziua(d,luna,y);
printf("%s",p);
getch();
}

Содержание работы:

Составьте программы для нижеследующих заданий. Запишите программы


на сервер и в тетрадь.

5.0. Наберите и выполните программы примеров 5.1-5.13.

5.1. Программа читает два слова и выводит наименьшее ( наибольшее )


слово.
Используйте функцию strcmp().

5.2. Программа читает два слова и выводит самое длинное ( самое


короткое ) слово.
Используйте функцию strlen().

5.3. Программа читает текст и выводит наибольшее ( наименьшее ) слово.


Используйте функции strcmp(), strcpy().

5.4. Программа читает текст и выводит самое короткое ( самое длинное )


слово.
Используйте функции strlen(), strcpy().

5.5. Программа читает два слова и выводит их в возрастающем


( убывающем ) порядке_ не различая прописные и заглавные буквы.
Используйте функции stricmp(), strcpy().

5.6. Программа читает два слова и выводит их в порядке убывания


( возрастания ) их длин.
Используйте функции strlen(), strcpy().
50
5.7. Программа читает текст, заканчивающийся символом EOF,
определяет наибольшее ( наименьшее ) слово, используя память heap, и
выводит это слово.
Используйте функцию из примера 5.5.

5.8. Программа читает текст, заканчивающийся символом EOF,


определяет самое короткое ( самое длинное ) слово, используя память heap,
и выводит это слово.
Используйте функцию из примера 5.5.

5.9. Программа читает текст, заканчивающийся символом EOF и


помещает его в память heap, затем упорядочивает его в порядке
возрастания ( убывания ) слов и выводит упорядоченный текст.
Используйте функцию из примера 5.6.

5.10. Программа читает текст, заканчивающийся символом EOF и


помещает его в память heap, затем упорядочивает его в порядке убывания (
возрастания ) длин слов и выводит упорядоченный текст.
Используйте функцию из примера 5.7.

5.11. Программа читает календарную дату в форме ddmmyy, где dd –


число, mm - месяц и yy - последние две цифры года для соответствующей
даты; проверяет, правильна ли прочитанная дата, и выводит ее в форме dd
luna yyyy, где luna - название месяца а yyyy ( 4 цифры ) - год заданной
даты.
Используйте функцию из примера 5.8.

5.12. Программа читает календарную дату в форме dd luna yyyy, где dd


- число, luna - название месяца и yyyy ( 4 цифры ) - год для
соответствующей даты; проверяет, правильна ли дата, и выводит ее в форме
dd/mm/yy, где mm – номер месяца, а yy - последние две цифры года
для прочитанной даты.
Используйте функцию из примера 5.9.

5.13. Программа читает два целых числа, представляющих собой


календарный год, и номер дня в этом году, затем определяет и выводит
календарную дату, соответствующую этому дню.
Используйте функцию из примера 5.11.

5.14. Программа читает название дня недели и определяет, сколько дней с


таким названием было с начала текущего года по текущий день,
включительно.

5.15. Программа читает две календарные даты и определяет, сколько дней


прошло с одной календарной даты до другой.

5.16. Программа читает две календарные даты и название дня недели,


затем определяет, сколько дней с этим названием было между заданными
календарными датами.
51
Контрольные вопросы:

5.1. Что возвращают функции: strcmp( “asem”, ”ASEM” ), stricmp(


“ASEM”, ”asem” ), strncmp( “Limbajul C”, ”Limbajul C++”, 10 ), strncmp(
“Limbajul C++”, ”Limbajul C”, 12 ), strncmp( “Limbajul C++”, ”Limbajul C”,
14 ), strnicmp( “ABRACADABRA”, ”arbadacarba”, 1 ) ? Объясните.
5.2. Что возвращают функции: strlen( “ASEM” ), strlen( “asem0” ), strlen(
“asem\0” ), strlen( “ASEM\n” ), strlen( “ASEM\N” ), strlen( “” ), strlen( “\0”
), strlen( “0” ), strlen( “Cibernetica\t” ) ? Объясните.
5.3. Каков будет результат: strcat( “Limbajul C”, ”++” ), strncat(
“Cibernetică, ”, ”Informatică Economică, Statistică, Previziune Economică”,
21 ) ? Объясните.
5.4. Пусть имеется char s[]=”abracadabra”, *p; p=strchr( s, ’a’ ); Что
выводит printf( “%c\t%d\t%p\n”, *p, p+1, p+6 ); ? Объясните.
5.5. Пусть имеется char b[]=”Cibernetica\n”, *r, a=’\n’; r=strchr( b, a );
Что выводит printf( “%c\n”, *r ); ? Объясните.
5.6. Что выводит puts( strstr( “Contabilitate financiara”, “abil” ) );
Объясните?
5.7. Пусть имеется char *s=”Informatica”, *p=”Cibernetica”,
*q=”Statistica”, *r=”Economica”; strcpy( s, p ); strcpy( p, q ); strcy( q, r );
strcpy( r, s ); Что выводит puts( s ); ? Объясните.
5.8. Пусть имеется char *p,*q,*r; Что выводят puts( strncpy( p, ”Limbajul
C++”, 10 ) ); puts( strncpy( q, ”Limbajul C++, 12 ) ); puts( strncpy( r,
”Limbajul C++, 14 ) ); ? Объясните.
5.9. Пусть имеется int *x; float *y; double *z; long double *w; x=(int
*)malloc( 5*sizeof( int ) ); y=(float *)malloc( 5*sizeof( float ) ); z=(double
*)malloc( 5*sizeof( double ) ); w=(long double *)malloc( 5*sizeof( double ) );
w=(long double *)malloc( 5*sizeof( long double ) ); Сколько байтов выделяется
при каждом вызове функции malloc() и каковы значения переменных x, y,
z, w ? Объясните.
5.10. Чем отличаются функции malloc() и calloc() ? Каково различие
между (int *)malloc( 5*sizeof( int ) ); и (int *)calloc( 5*sizeof( int ) ); ?
Объясните.
5.11. Пусть имеется char *s=”Costel, Vicu и Andrei идут в театр”,
*q=”Vicu”; puts( strstr( s, q ) ); Кто идет в театр? Объясните.
5.12. Рассмотрите вопросы теста тест_05 и выберите правильные ответы
на каждый вопрос.

52
Лабораторная работа N 6

Тема: Типы данных, определяемые пользователем

Цель работы: Использование типов данных, определяемых


пользователем ( структура, объединение, битовые поля, перечисление );
использование ключевого слова typedef для переименования типов.

Указания:

Пользователь может определить и использовать в программе свои типы


данных. Могут быть определены данные типа структура, объединение,
битовые поля, перечисление и др.

Одним из способов группировки данных является массив. Массив


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

Объявление структуры имеет вид:

struct name { список объявлений } name1, name2, … namen;

Здесь name определяет новый тип данных, а name1, namе2, …, namen


являются структурами, т.е. переменными типа name . список объявлений
объявляет компоненты структуры типа name. Среди name1, name2, …,
namen могут быть и имена массивов. В таком случае элементы этих
массивов являются структурами типа name. В объявлении структуры имя
типа, т.е. name, или группа имен переменных, т.е. name1, name2, …,
namen, могут отсутствовать, но не одновременно. Некоторые компоненты
структуры, объявленные в списке объявлений, могут в свою очередь быть
другими структурами.

Другая структура этого же типа name ( тип, определенный выше ) может


быть объявлена следующим образом:

struct name name_of_structure;

Примеры объявления структур:

1) struct punct { int x; int y; } a, b, c, *w;

Это объявление определяет тип punct и объявляет a, b, c структурами


типа punct, т.е. структурами с двумя компонентами типа int, и объявляет
указатель w к типу punct.
53
2) В продолжении могут быть объявлены и другие структуры типа punct.
Например, struct punct d, e={ 10, 20 }, *q, f[11]; объявляет пременные d, e
структурами типа punct, определенного выше ( кроме того, структура e
проинициализирована ); объявляет указатель q к типу punct, и объявляет
массив f структур этого же типа punct.

1) struct dosar
{
char nume[25];
char prenume[25];
struct data_calenraristica
{ int zi;
char luna[11];
int an;
} data_nasterii, data_angajarii;
} angajat, secţie[100];

Ссылки на элементы структуры производятся посредством конструкций


формы m.n или p->n, где m - имя структуры, а n - имя компоненты, на
которую производится ссылка, и p - указатель на структуру. Так, в примере
1), a.x означает компоненту x структуры a типа punct, а w->y
представляет компоненту y структуры типа punct, на которую указывает
поинтер w.

При выделении памяти для структуры, каждой ее компоненте


выделяется необходимое количество байтов, в соответствии с типом этой
компоненты. Так, в примере 1), каждой объявленной структуре будет
выделено 2+2=4 байта, т.к. они имеют по 2 компоненты, требующих по 2
байта каждая.

Еще одним способом группировки данных является объединение. В


этом случае группировка производится с целью хранения в одной и той же
зоне памяти данных разного типа в разные моменты времени.
Объявление объединения производится так же, как и объявление
структуры. Различие лишь в том, что слово struct заменяется на слово
union.

Значит, объявление объединения имеет вид:

union name { список объявлений } name1, name2, …, namen;

Пример объявления объединений:

union aria { int x; // 2 octeţi


float r; // 4 octeţi
double w; // 8 octeţi
char t[5]; // 5*1=5 octeţi
} f1, f2, f3[6], *z;

54
Здесь объявлены объединения f1, f2 типа aria, массив объединений f3,
состоящий из 6 элементов, представляющих собой объединения типа aria, а
также указатель z, указывающий на тот же тип aria.

При выделении памяти, в отличие от структуры, объединению выделяется


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

Ссылка на элементы объединения производится также, как и в случае


структур.

Другим типом данных, определяемым пользователем, является тип


перечисление. Этот тип позволяет использовать смысловые имена для
числовых значений. Объявление типа перечисление подобно объявлению
структуры.

Объявление типа перечисление имеет вид:

enum name { name0, name1, …, namen } v1, v2, …, vm;

Здесь name - имя объявленного нового типа, а namе1, name2, …,


namen - смысловые имена, используемые впоследствии вместо числовых
значений, а именно: name0 заменяет значение 0, name2 заменяет
значение 1, …, namen заменяет значение n. v1, v2, …, vm - объявлены
переменными типа name, т.е. переменными этого нового типа перечисления.
Эти переменные подобны переменным типа int. Таким образом, при таком
объявлении, смысловые имена name0, name1, …, namen неявно заменяют
целые значения 0, 1, …, n.

При объявлении типа перечисление для смысловых имен можно явно


определить и другие значения. Например, если использовать конструкцию
namei=ci вместо namei в списке смысловых имен, то это приведет к
определению для имени namei значения выражения ci. Выражение ci
должно быть постоянным. Если для смыслового имени в объявлении не
указано явно никакого значения, то оно имеет значение предыдущего имени,
увеличенное на 1. Первому смысловому имени, т.е. name0, по умолчанию
присваивается значение 0.

Примеры объявления типа перечисление:

1) enum luna { ileg, ian, feb, mar, apr, mai, iun, iul, aug, sep, oct, nov,
dec } m1, m2, m3[12], *w;

2) enum mon { ian=1, feb, mar, apr, mai, iun, iul, aug, sep, oct, nov,
dec };

3) enum boolean { false, true };


55
enum boolean bisect;

4) enum { luni=1, marti, miercuri, joi, vineri, simbata, duminica } zi;

Существующему типу данных ( предопределенному или определенному


пользователем ) можно присвоить другое имя. После переименования типа
можно объявить переменные этого типа, указав в объявлении новое имя.

Такое объявление типа имеет вид:

typedef old_tip_name new_tip_name;

Примеры переименования типов:

1) typedef int INTEGER;


INTEGER x,y;

2) typedef struct { double real; double imag; } COMPLEX;


COMPLEX w, tz[11];

3) typedef struct { int x; int y; } POINT;


POINT a,b;

1) typedef union { double raza; double tp; double ld[2]; double lt[3]; }
figura;
figura cerc, patrat, triunghi;

В программе можно определить данные для побитовой обработки. В этом


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

Объявление типа битовые поля имеет вид:

struct name { pole1; pole2; …; polen; } n1, n2, …, nm;

Здесь name - имя объявленного нового типа битовые поля, а n1, n2,
…, nm - переменные этого нового типа, т.е. каждая из них является
структурой битовых полей, а pole1, pole2, …, polen - объявления битовых
полей.

Объявление битового поля имеет вид:

tip imea_polea dlina_polea;

56
где tip - тип поля, imea_polea - имя поля, dlina_polea - длина поля в
битах, не превышающая длину машинного слова. Имя поля может
отсутствовать, тогда поле определяет зону памяти, неиспользованную в
соответствующем слове. Длина поля может быть 0, тогда следующее за ним
поле размещается в следующем слове. Биты размещаются в машинном слове,
начиная с битов меньшего порядка в сторону возрастания порядка битов.
Если поле не умещается в текущем слове, тогда оно размещается в
следующем слове. Поля могут быть типа unsigned, int, signed, char, usigned
char.

Ссылка на битовые поля производится как и в случае обычных структур.

Пример объявления типа битовые поля:

struct { unsigned a:2;


int b:2;
unsigned :3;
unsigned c:2;
unsigned :0;
int d:5;
unsigned e:5; } x,y;

Здесь объявлены 2 переменные x, y типа битовые поля. Имя типа


отсутствует, т.е. тип является анонимным. Переменная x размещается в 2
машинных словах следующим образом: в первом слове биты 0 и 1
предназначены для поля a, следующие 2 бита, т.е. биты 2 и 3,
предназначены для поля b, следующие 3 бита, т.е. биты 4, 5, 6, не
используются, потому что в объявлении отсутствует имя соответствующего
поля. В продолжении, биты 7, 8 выделяются для поля c. Длина следующего
поля в объявлении равна 0, следовательно, поле d, следующее за ним, будет
размещаться в следующем машинном слове. Таким образом, полю d будут
выделены биты 0, 1, 2, 3, 4 второго машинного слова. И, наконец,
последнему полю e будут выделены биты 5, 6, 7, 8, 9 второго машинного
слова.
Для этого примера, присвоение x.a=1; приведет к присвоению полю a
значения 1, т.е. бит 0 примет значение 1, а бит 1 примет значение 0.
Аналогично, присвоение x.b=-1; приведет к присвоению полю b значения -
1, т.е. биты 2 и 3 примут значение 1, т.к. представлением числа -1 на
двух битах - 11.

Нельзя использовать массивы битовых полей.


К полю нельзя применять операцию адреса &.

В объявлении структуры можно комбинировать обычные компоненты


структуры с битовыми полями.

Примеры программ:

57
Пример 6.1. Функция вычисляет аргумент комплексного числа. У нее
один параметр, являющийся указателем на тип COMPLEX. Тип COMPLEX
определяется следующим образом: typedef struct { double x; double y }
COMPLEX; Функция возвращает аргумент типа double комплексного числа,
на которое указывает параметр функции.
Для комплексного числа z=x+iy аргумент argz равен:

0, если x≥0 и y=0;


π/2, если x=0 и y≥0;
π, если x<0 и y=0;
3π/2, если x=0 и y<0;
arctg( y/x ), если x>0 и y>0;
arctg( y/x )+π,если x<0 и y≠0;
2π+arctg( y/x ), если x>0 и y<0.

double darg(COMPLEX *w)


{ double a;
if( (w->x >= 0) && (w->y == 0) ) return 0.0;
if( (w->x == 0) && (w->y >=0) ) return PI/2;
if( (w->x < 0) && (w->y == 0) ) return PI;
if( (w->x == 0) && (w->y < 0) ) return 3*PI/2;
a=atan( w->y / w->x );
if( (w->x < 0) && (w->y != 0) ) return a+PI;
if( (w->x > 0) && (w->y < 0) ) return a+2*PI;
return a;
}

Пример 6.2. Программа читает два комплексных числа и выводит эти


числа в возрастающем порядке их аргументов. Используется функция darg()
из примера 6.1, которая сохранена в файле “f:\bc\mydir\ex6_1.c”.

#include <conio.h>
#include <stdio.h>
#include <math.h>
#define PI 3.14159265358979
typedef struct { double x; double y; } COMPLEX;
#include “f:\bc\mydir\ex6_1.c”
void main(void)
{ COMPLEX z1,z2;
double argz1, argz2;
clrscr();
printf(“Introduceţi 2 numere complexe: ”);
scanf(“%lf %lf %lf %lf”,
&z1.x,&z1.y,&z2.x,&z2.y);
argz1=darg(&z1); argz2=darg(&z2);
printf(“\n\nAţi introdus numerele
complexe:\n\n”);
printf(“z1=(%-.2lf)+i(%-.2lf)\n
z2=(%-.2lf+i(%-.2lf)\n\n”,
z1.x,z1.y,z2.x,z2.y);
58
printf(“argz1=%-lf\nargz2=%-lf\n”,argz1,argz2);
printf(“\nNumerele ordonate în ordinea creşterii
argumentelor:\n\n”);
(argz1<argz2)?printf(“(%-.2lf)+i(%-.2lf)\n
(%-.2lf)+i(%-.2lf)\n”, z1.x,z1.y,z2.x,z2.y):
printf(“(%-.2lf)+i(%-.2lf)\n(%-.2lf)+i(%-.2lf)\n”,
z2.x,z2.y,z1.x,z1.y);
getch();
}

Пример 6.3. Программа выводит на экран в алгебраической форме все


корни порядка n из комплексного числа z, заданного в алгебраической
форме. Числа n и z вводятся с клавиатуры. Программа использует
функцию darg() из примера 6.1 для определения аргумента числа z.
Функция darg() сохранена в файле “f:\bc\mydir\ex6_1.c”.
Для вычисления корней порядка n из комплексного числа,
представленного в алгебраической форме z=x+iy, число z представляется
сначала в тригонометрической форме z=r⋅(cosϕ+isinϕ), где r - модуль числа
z, а ϕ - аргумент числа z. Затем вычисляются все комплексные числа,
являющиеся корнями порядка n из числа z. Существуют n таких чисел:
rm=r1/n⋅(cos( (ϕ+2⋅π⋅m)/n )+isin( (ϕ+2⋅π⋅m)/n ), m=0, 1, …, n-1.

#include <conio.h>
#include <stdio.h>
#include <math.h>
#define PI 3.14159265358979
typedef struct { double x; double y; } COMPLEX;
#include “f:\bc\mydir\ex6_1.c”
void main(void)
{ COMPLEX z;
double modz, argz, mod, arg;
unsigned n, m;
clrscr();
printf(“Introduceţi ordinul n pentru rădăcină
şi numărul complex z:\n\n”);
scanf(“%u %lf %lf”, &n, &z.x, &z.y );
printf(“\n\nAţi introdus n=%-u şi
z=(%.2lf)+i(%.2lf)\n\n”, n, z.x, z.y );
//Calculăm modulul numărului z
modz=sqrt( z.x*z.x+z.y*z.y );
//Calculăm argumentul numărului z
argz=darg(&z);
//Calculăm modulul pentru rădăcini
mod=pow( modz, 1.0/n );
//Calculăm rădăcinile de ordinul n din z
printf(“Rădăcinile de ordinul %d din numărul
z=(%.2lf)+i(%.2lf) sunt:\n\n”,
n, z.x, z.y );
for(m=0; m<n; m++)
{ arg=(argz+2*PI*m)/n;
59
printf(“r(%2d)=(%-.2lf)+i(%-.2lf)\n”,
m, mod*cos(arg), mod*sin(arg) ); }
getch();
}

Пример 6.4. Функция сокращает рациональную дробь. У нее один


параметр, являющийся указателем на тип RATIONAL, который указывает
на сокращенную дробь. Тип RATIONAL определяется следующим образом:
typedef struct { long unsigned m; long unsigned n; } RATIONAL; Функция
возвращает указатель типа RATIONAL *, указывающий на сокращенную
дробь. Сокращение дроби производится делением знаменателя и числителя
на их наибольший общий делитель.

RATIONAL *simplifica( RATIONAL *f)


{ static RATIONAL s;
long unsigned k,h, cmmdc=1;
/* h este minimul dintre numitorul şi
numărătorul fracţiei f */
h=(f->m)<(f->n)?(f->m):(f->n);
/* cmmdc este cel mai mare divizor comun
pentru numărător şi numitor */
for(k=1; k<=h; k++)
if( (f->m) % k == 0 && (f->n) % k == 0 )
cmmdc=k;
s.m=(f->m)/cmmdc; s.n=(f->n)/cmmdc;
return &s;
}

Пример 6.5. Программа читает рациональную дробь, сокращает ее, если


возможно, и выводит сокращенную дробь на экран. Используется тип
RATIONAL и функция simplifica() из примера 6.4, сохраненная в файле
“f:\bc\mydir\ex6_4.c”.

#include <conio.h>
#include <stdio.h>
typedef struct { long unsigned m; long unsigned n; }
RATIONAL;
#include "f:\bc\mydir\ex6_4.c"
void main(void)
{ RATIONAL *p, frac;
clrscr();
printf("Introduceţi o fracţie raţională:\n\n");
scanf("%lu %lu", &frac.m, &frac.n);
printf("\n\nAţi introdus fracţia:\n\n");
printf("%lu/%lu\n\n", frac.m, frac.n);
p=simplifica( &frac );
printf("Fracţia simplificată este:\n\n");
printf("%lu/%lu",p->m,p->n);
getch();
}
60
Пример 6.6. Программа вычисляет объем одного из тел: куба, шара,
прямого конуса, прямого цилиндра, прямого параллелипипеда или прямой
пирамиды. Величины, необходимые для вычисления объема, будут храниться
в объединении. Объемы вычисляются по формулам:

куб - a3, где a - сторона куба;


шар - 4/3⋅π⋅r3, где r - радиус шара;
конус - 1/3⋅π⋅r2⋅h, где h - высота конуса, а r - радиус окружности его
основания;
цилиндр - π⋅r2⋅h, где h - высота цилиндра, а r - радиус окружности его
основания;
прямой параллелипипед - a⋅b⋅c, где a, b, c - ребра параллелипипеда;
пирамида - 1/3⋅a⋅b⋅h, где h - высота пирамиды, а a, b - стороны
прямоугольника, являющегося основанием пирамиды.

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#define PI 3.14159265358979
void main(void)
{ union { double r;
double a[2];
double b[3]; } c;
int v; double volum; clrscr();
printf(“Alegeţi o alternativă:\n\n”);
printf(“1 – cub\n
2 – sferă\n
3 – con\n”);
printf(“4 – cilinrdu\n
5 – paralelepiped\n
6 – piramidă\n\n”);
scanf(“%d”,&v); printf(“\n\n”);
switch(v) {
default:printf(“Aţi ales alternativa de
ieşire”); getch(); exit(1);
case 1: printf (“Introduceţi lungimea
laturii cubului: ”);
scanf(“%lf”, &c.r);
volum=c.r*c.r*c.r;
printf(“\n\nVolumul cubului cu
latura a=%-.2lf este ”); break;
case 2: printf(“Introduceţi lungimea razei
sferei: ”);
scanf(“%lf”, &c.r);
volum=4.0/3.0*PI*c.r*c.r*c.r;
printf(“\n\nVolumul sferei cu raza
r=%-.2lf este ”); break;
case 3: printf(“Introduceţi lungimea razei
circumferinţei din baza conului”);
61
printf(“ şi înălţimea conului: ”);
scanf(“%lf%lf”,&c.a[0],&c.a[1]);
volum=1.0/3.0*PI*c.a[0]*c.a[0]*c.a[1];
printf(“\n\nVolumul conului cu raza
bazei”);
printf(“ r=%-.2lf şi înălţimea h=%-
.2lf este ”); break;
case 4: printf(“Introduceţi lungimea razei
circumferinţei din baza cilindrului”);
printf(“ şi înălţimea cilindrului: ”);
scanf(“%lf%lf”,&c.a[0],&c.a[1]);
volum=PI*c.a[0]*c.a[0]*c.a[1];
printf(“\n\nVolumul cilindrului cu
raza ”);
printf(“bazei r=%-.2lf şi înălţimea
h=%-.2lf este ”); break;
case 5: printf(“Introduceţi lungimile
laturilor paralelipipedului: ”);
scanf(“%lf%lf%lf”,
&c.b[0],&c.b[1],&c.b[2]);
volum=c.b[0]*c.b[1]*c.b[2];
printf(“\n\nVolumul papalelipipedului
cu laturile ”);
printf(“a=%-.2lf, b=%-.2lf şi
c=%-.2lf este ”); break;
case 6: printf(“Introduceţi lungimile
laturilor dreptunghiului ”);
printf(“din baza piramidei ”);
printf(”si inaltimea piramidei: ”);
scanf(“%lf%lf%lf”,
&c.b[0],&c.b[1],&c.b[2]);
volum=1.0/3.0*c.b[0]*c.b[1]*c.b[2];
printf(“\n\nVolumul piramidei cu
laturile dreptunghiului ”);
printf(“din bază a=%-.2lf, b=%-.2lf
şi înălţimea h=%-.2lf este ”);
}
printf(“%-.2lf\n\n”,volum);
getch();
}

Пример 6.7. Программа читает номер календарного месяца и выводит


название времени года, к которому относится соответствующий месяц.
Используется тип перечисление для месяцев и времен года. Времена года
пронумерованы следующим образом: Весна=1, Лето=2, Осень=3, Зима=4.

#include <conio.h>
#include <stdio.h>
void main(void)
{ unsigned nl;
62
enum { Ian=1, Feb, Mar, Apr, Mai, Iun,
Iul, Aug, Sep, Oct, Nov, Dec } dl;
enum { Primăvara=1, Vara, Toamna, Iarna } da;
while(1)
{ clrscr();
printf(“Introduceţi numărul unei luni
calendaristice: ”);
if( scanf(“%u”, &dl)==1 )
if( dl>0 && dl<13 ) break; }
if( dl==Dec||dl==Ian||dl==Feb )
{ da=Iarna; printf(“%u – Iarna\n”, da); }
if( dl==Mar||dl==Apr||dl==Mai )
{ da=Primăvara; printf(“%u – Primăvara\n”,
da); }
if( dl==Iun||dl==Iul||dl==Aug )
{ da=Vara; printf(“%u – Vara\n”, da); }
if( dl==Sep||dl==Oct||dl==Nov )
{ da=Toamna; printf(“%u – Toamna\n”, da); }
getch();
}

Пример 6.8. Целое цисло без знака представлено в памяти двумя


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

#include <conio.h>
#include <stdio.h>
void main(void)
{ union { struct {
unsigned b0_3 : 4;
unsigned : 4;
unsigned : 4;
unsigned b12_15: 4;
} nb;
unsigned n; } nu;
clrscr();
printf("Inroduceţi un număr întreg fără semn: ");
scanf("%u",&nu.n);
printf("%u\n",nu.nb.b12_15);
printf("%u",nu.nb.b0_3);
getch();
}

Содержание работы:

Составьте и выполните программы для нижеследующих заданий.


Запишите программы на сервер и в тетрадь.

63
6.0. Введите и выполните программы из примеров 6.1-6.8.

6.1. Программа выполняет операции с комплексными числами ( ввод,


вывод, присвоение, вычисление модуля, вычисление аргумента, изменение
знака, сложение, вычитание, произведение, деление, представление в
тригонометрической форме ).

6.2. Программа вычисляет zn для комплексных чисел.

6.3. Программа вычисляет значение многочлена степени n с


комплексными коэффициентами и комплексной переменной.

6.4. Программа решает уравнение z2+b⋅z+c=0, где a, b, c, z -


комплексные числа.

6.5. Программа выполняет операции над рациональными дробями


( ввод, вывод, присвоение, обращение, сложение, вычитание, умножение,
деление ).

6.6. Программа вычисляет значение рационального выражения.

6.7. Программа рисует, выводя один и тот же символ на экране:


горизонтальный отрезок, вертикальный отрезок, наклонный отрезок,
прямоугольник ( проверьте принадлежит ли точка ( x, y ) экрану ! ).

6.8. Программа рисует фигуру, используя элементы задания 7.

6.9. Программа вычисляет площадь фигуры, которая может быть:


кругом, квадратом, прямоугольником, треугольником.
Используйте тип union !

6.10. Программа выполняет операции с векторами и матрицами


( ввод, вывод, присвоение, сложение, вычитание, умножение,
транспонирование ).

6.11. Программа вычисляет An для матрицы A.

6.12. Программа вычисляет det(A) - определитель матрицы A.

6.13. Программа вычисляет обратную матрицу.

6.14. Программа решает уравнение A⋅x=b обращением матрицы A.

6.15. Программа определяет название дня по календарной дате, которая


предполагается правильной.
Используйте тип enum !

Контрольные вопросы:

64
6.1. Что такое тип определенный пользователем ? Какие это типы?
6.2. Что такое структура ? Каково различие между структурой и
массивом?
6.3. Как объявляется структура?
6.4. Как производится ссылка на компоненты структуры?
6.5. Как выделяется память для структур ?
6.6. С какой целью используются данные типа структуры?
6.7. Что такое структура анонимного типа?
6.8. Что такое объединение ? Чем отличается объединение от
структуры?
6.9. Как объявляется объединение?
6.10. Как производится ссылка на компоненты объединения?
6.11. Как выделяется память для объединений?
6.12. С какой целью используется тип объединение?
6.13. Что такое объединение анонимного типа?
6.14. Что представляет собой тип битовые поля ?
6.15. Как объявляются данные типа битовые поля?
6.16. Как производится ссылка на битовые поля?
6.17. Как выделяется память для данных типа битовые поля?
6.18. С какой целью используется тип битовые поля?
6.19. Какого типа могут быть битовые поля?
6.20. Какова максимальная длина битового поля?
6.21. Какова минимальная длина битового поля и что она означает?
6.22. Что означает отсутствие имени битового поля в объявлении
соответствующего типа битовые поля?
6.23. Что представляет собой тип перечисление ?
6.24. Как объявляется тип перечисление?
6.25. Как выделяется память для данных типа перечисление?
6.26. В каких целях используется тип перечисление?
6.27. Какие значения неявно присваиваются смысловым именам в
объявлении типа перечисление?
6.28. Как можно присвоить смысловым именам из объявления типа
перечисление значения отличные от неявных значений?
6.29. Каков тип значений смысловых имен в объявлении типа
перечисление?
6.30. Какие операции можно производить над данными типа
перечисление?
6.31. Как можно переименовать тип?
6.32. Рассмотрите вопросы теста тест_06 и выберите правильные ответы
на каждый вопрос.

65
Лабораторная работа N 7

Тема: Аргументы функции main()

Целъ работы: Передача и правильное использование аргументов


функции main()

Указания:

Программе на языке С ( С++ ) можно передать различные параметры, т.е.


аргументы. Чтобы программа могла исполъзовать эти параметры функция
main() в программе должна иметь заголовок tip main( int argc, char
*argv[] ). Значение аргумента argc равно количеству аргументов,
увеличенному на 1, а argv[] – массив указателей на зоны памяти, где
хранятся аргументы. Аргументы хранятся в форме символьных
последовательностей. Указатель argv[0] всегда указывает на символьную
последовательность, содержащую путь файла с выполняемым образом
программы ( .exe ). Указатель argv[1] указывает на символьную
последовательность, содержащую первый аргумент, указатель argv[2]
указывает на символьную последовательность, содержащую второй аргумент
и т.д. Таким образом, аргументы функции main() являются символьными
последовательностями.
Если программа запускается в командной строке MS DOS, тогда после
имени программы, через пробел, набираются с клавиатуры аргументы
функции, разделенные между собой пробелами, и только затем нажимается
клавиша Enter.
Если программу запускают в среде Borland C++, тогда параметры
программы определяются в подменю Run-Arguments. При выборе этого
подменю на экране высвечивается окно Program Arguments, в котором
набираются аргументы функции, разделенные пробелами, и затем
нажимается клавиша Enter или кнопка OK. После этого запускается
программа на выполнение.

Примеры программ:

Пример 7.1. Программа выводит аргументы функции main().

#include <conio.h>
#include <stdio.h>
void main(int argc, char *argv[])
{ int i=0;
clrscr();
for( ; i++<argc-1; )
printf(“%s\n”,argv[i]);
getch();
}
66
Пример 7.2. Программа определяет количество дней в календарном
месяце. Название месяца и соответствующий год являются аргументами
функции main().

#include <conio.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void main(int a, char *b[])
{ char *dl[]=
{"","Ianuarie", "Februarie", "Martie",
"Aprilie", "Mai", "Iunie",
"Iulie", "August", "Septembrie",
"Octombrie", "Noiembrie", "Decembrie"};
int i,bi,an,nz[]=
{0,31,28,31,30,31,30,31,31,30,31,30,31};
clrscr(); an=atoi(b[2]);
bi=(an%4==0)&&(an%100)||(an%400==0);
if( bi ) nz[2]+=1;
for(i=1; i<13; i++)
if( !strcmp(b[1],dl[i]) )
{ printf("\n%s - %d zile\n",b[1],nz[i]);
break; }
getch();
}

Пример 7.3. Функция, вызванная функцией main(), в свою очередь,


может вызвать функцию main(). В следующей программе функция имеет
два параметра. Она вызывает функцию с(), которая требует ввести 2 числа.
Если вводятся 2 отличных от нуля числа, то функция возвращает сумму
чисел, обратных к введенным. В зависимости от знака возвращенной суммы,
функция main() выводит первый или второй свой аргумент. Если функция
с() вводит хотя бы одно нулевое число, тогда она вызывает функцию
main().

#include <conio.h>
#include <stdio.h>
void main(int p, char **w)
{ double c(int, char **),k;
k=c(p,w);
if( k>0 ) printf("\n\nargv[1]: %s\n",w[1]);
else printf("\n\nargv[2]: %s\n",w[2]);
getch(); }

double c(int a, char **b)


{ double k; int m,n; clrscr();
printf("Introduceti 2 numere: ");
scanf("%d %d",&m,&n);
if( m==0||n==0 ) main(a,b);
67
k=1.0/(double)m + 1.0/(double)n;
return k; }

Пример 7.4. Функция main() может вызвать саму себя. В этом примере
функция main() имеет один параметр – название определенного языка
программирования. Программа должна “угадать” это название, прочитав
название какого-либо языка со стандартного ввода.

#include <conio.h>
#include <stdio.h>
#include <string.h>
void main(int a, char *b[])
{ char *s; clrscr();
printf("Limbajul de programare ? ");
gets(s);
if( strcmp(s,b[1]) )
{ printf("Greşit ! Apăsaţi o tastă
!\n");
getch(); main(a,b); }
printf("Corect ! Aţi ghicit !
Apăsaţi o tastă !\n");
getch(); exit(1);
}

Содержание работы:

Составьте и выполните программы для нижеследующих заданий.


Запишите программы на сервер и в тетрадь.

7.0.Введите и выполните программы из примеров 7.1-7.4.

7.1.Программа выводит на экран некоторый текст несколько раз. Текст и


количество выводов являются параметрами функции main().

7.2.Программа выводит аргументы функции main() двумя способами: в


форме символьных последовательностей ( со спецификатором формата %s )
и раздельно, по одному символу ( со спецификатором формата %с ).

7.3.Программа выводит аргументы функции main(), включая argc.

7.4.Программа выводит на экран в виде mm/dd/20yy календарную дату,


заданную в командной строке MS DOS в виде dd month yy. Здесь dd –
число, month – название месяца, mm - номер месяца, и yy – две
последние цифры года, соответствующей календарной даты.

Контрольные вопросы:

7.0.Каков тип параметров функции main()?


68
7.1.Как определяются ( как вводятся ) параметры функции main() через
командную строку MS DOS ?
7.2.Как определяются параметры функции main() в среде Borland C++ ?
7.3.Что представляет собой первый параметр функции main() и каков его
тип?
7.4.Каков тип второго параметра функции main() и каково его значение?
7.5.Что представляет собой первый указатель из параметров функции
main(), на что он указывает?
7.6.Какой параметр функции main() указывает на символьную
последовательность, содержащую путь к файлу с выполняемой программой?
7.7.Как можно изменить значение параметра функции main(), на которое
указывает argv[0] ?
7.8.Можно ли в программе изменить значения параметров функции
main() ?
7.9.Пусть Monday, Tuesday, Thursday – введенные параметры для
функции

main( int a, char *v[] )


{ char *b=”Monday”, *c=”Tuesday”, *d=”Thursday”;
strcpy( v[1], d ); strcpy( v[a], b );
printf(”%d\t%s\t%s\t%s\n”,a,v[1],v[2],v[3]);
}

Что будет выведено на экране?

7.10. Рассмотрите вопросы теста тест_07 и выберите правильные ответы


на каждый вопрос.

69
Лабораторная работа N 8

Тема: Рекурсии

Цель работы: Использование рекурсивных функций при решении задач


обработки данных.

Указания:

Рекурсивные функции используются при программировании


рекурсивных вычислительных процессов. Вычислительный процесс
называется рекурсивным, если часть процесса определяется через этот же
процесс. Значит функция, которая вызывает саму себя ( непосредственно
или косвенно при помощи других функций ), называется рекурсивной.
Например, функция вычисления факториала fact( n ) является рекурсивной.
Она определяется при целом n≥0 следующим образом: fact( n )=1, если
n=0, и fact( n )=n⋅fact( n-1 ), если n>0.
Рекурсивный процесс должен содержать часть, которая не определяется
через него самого. В примере функции fact( n ), альтернатива fact( n )=1,
если n=0, является частью, которая определяется непосредственно, а не
через процесс вычисления факториала.
На языке С рекурсивная функция fact( n ) может быть реализована
следующим образом:

double fact( int n )


{ if (n==0) return 1.0;
else return n*fact(n-1); }

Функция sscanf( p, c, p1, p2, … ) аналогична функции scanf(). У нее на


один параметр больше, а именно на первый параметр p. Остальные
параметры c, p1, p2, … такие же как у функции scanf(). Как правило, р -
имя массива типа char. Значением этого параметра является адрес начала
зоны памяти, где могут храниться символы ASCII. Функция sscanf(), как и
scanf(), читает данные и преобразовывает их из внешнего формата во
внутренний. Но функция sscanf() читает данные не из буфера клавиатуры,
как scanf(), а из зоны памяти, указанной первым параметром р. В эту зону
данные вводятся, например, с помощью функции gets(). Таким образом
можно устранить ошибки, допущенные при наборе данных с клавиатуры.

Примеры программ:

Пример 8.1. Программа читает целое число n из отрезка [ 0;170 ] и


вычисляет n! ( n факториал ), используя определенную выше рекурсивную
функцию fact(n).

#include <stdio.h>
70
#include <conio.h>
#define MAX 170
void main(void)
{ double fact(int n);
int n; char t[255];
clrscr();
for ( ; ; )
{ printf(“n=? “);
if( gets(t)>0 && sscanf(t,”%d”,&n)==1 &&
n>=0 && n<=MAX ) break;
printf(“Se cere un număr întreg din
segmentul [0,%d]\n”,MAX); }
printf(“n=%d\tn!=%g\n”,n,fact(n));
getch();
}
double fact(int n)
{ if( n==0 ) return 1.0;
else return n*fact(n-1); }

Пример 8.2. Программа вычисляет число размещений A(n,k) из n


предметов по k, где 1≤k≤n≤170. Функцию A(n,k) можно определить
следующим образом: A(n,1)=n для k=1 и A(n,k)=(n-k+1)⋅A(n,k-1) для k=2,
3 , …, n. Следовательно, используется рекурсивная функция.

#include <stdio.h>
#include <conio.h>

unsigned A(unsigned n, unsigned k)


{ if( k==1 ) return n;
else return (n-k+1)*A(n,k-1);
}

void main(void)
{ unsigned n=17, k=7, aranjamente;
clrscr();
aranjamente=A(n,k);
printf(“A(%-u,%-u)=%-u”,n,k,aranjamente);
getch();
}

Пример 8.3. Программа вычисляет сумму квадратов чисел из отрезка [


m; n ] ( m<n ). Используется рекурсивная функция для вычисления суммы.

#include <stdio.h>
#include <conio.h>

unsigned Suma(unsigned m, unsigned n)


{ if( m==n ) return m*m;
else return m*m+Suma(m+1,n); }

71
void main(void)
{ unsigned m=1,n=10;
clrscr();
printf(“%-u\t%-u\tSuma=%-u”,m,n,Suma(m,n));
getch();
}

Пример 8.4. Программа определяет наименьший элемент числовой


последовательности. Используется рекурсивная функция.

#include <stdio.h>
#include <conio.h>
int x[4]={ 23,-34,43,-3 };
void main(void)
{ int Mic(int );
int n=3;
clrscr();
printf(“Cel mai mic număr este %d”, Mic(n));
getch();
}
int Mic(int n)
{ if( n==0 ) return x[n];
else return x[n]<Mic(n-1)?x[n]:Mic(n-1); }

Пример 8.5. Программа читает последовательность чисел и выводит их


сумму, используя рекурсивную функцию. За последним числом
последовательности набирается ^z, т.e. EOF.

#include <stdio.h>
#include <conio.h>
void citex(void)
{ int *x, *s=0;
if( scanf(“%d”,x)!=1 ) { printf(“%d”,*s);
return; }
else { *s+=*x; citex(); }
}
void main(void)
{ clrscr();
citex();
getch();
}

Пример 8.6. Программа выводит элементы матрицы в порядке,


напоминающем форму спирали, начиная с элемента первой строки и первого
столбца направо. Используется рекурсивная функция.

#include <stdio.h>
#include <conio.h>

int A[3][4]={ 1, 2, 3, 4,
72
10, 11, 12, 5,
9, 8, 7, 6 };

void Tipar(int i, int j, int n, int m)


{ if( i>m||j>n) return;
else { int k;
for(k=j; k<m; k++)
printf("%d\t",A[i][k]);
for(k=i; k<n; k++)
printf("%d\t",A[k][m]);
for(k=m; k>j; k--)
printf("%d\t",A[n][k]);
for(k=n; k>i; k--)
printf("%d\t",A[k][j]);
i++; j++; n--; m--;
Tipar(i,j,n,m); }
}

void main(void)
{ clrscr();
Tipar(0,0,2,3);
getch();
}

Содержание работы:

Составьте и выполните программы для нижеследующих заданий.


Запишите программы на сервер и в тетрадь.

8.0. Введите и выполните программы из примеров 8.1-8.6.

8.1.Программа вычисляет число размещений A(n,k) из n предметов по k,


где 1≤k≤n≤170. Функцию A(n,k) можно определить следующим образом:
A(n,k)=n!/(n-k)!
Используйте рекурсивную функцию вычисления факториала.

8.2.Программа вычисляет число сочетаний C(n,m) из n предметов по m,


где 1≤m≤n≤170. Функцию C(n,m) можно определить следующим образом:
C(n,m)=n для m=1 и C(n,m)=C(n,m-1)/(m⋅(n-m)) для m=2, 3, …, n. Таким
образом, можно использовать рекурсивную функцию.

8.3. Программа находит наибольший общий делитель cmmdc(m,n) двух


целых чисел без знака m и n.

8.4.Рекурсивную функцию, реализующую алгоритм Евклида и


определяющую cmmdc(m,n), можно определить следующим образом:
cmmdc(m,n)=m, если n=0 и m≠0, cmmdc(m,n)=n, если m=0 и n≠0, и
cmmdc(m,n)=cmmdc(n,m%n), если m>n>0. Для m=n=0 cmmdc(m,n) не
существует.
73
8.5. Программа находит наименьшее общее кратное cmmmc(m,n) двух
целых чисел без знака m и n.

8.6.Наименьшее общее кратное может быть вычислено по формуле


cmmmc(m,n)=m⋅n/cmmdc(m,n) ( смотри программу 8.3 ).

8.7.Программа находит числа Фибоначи f(n), которые для n≥0


определяются следующим образом: f(n)=0, если n=0, f(n)=1, если n=1,
f(n)=f(n-1)+f(n-2), если n=2, 3, … .

8.8.Программа вычисляет значения функции Аккермана a(m,n), которая


для m≥0, n≥0 определяется следующим образом: a(m,n)=n+1, если m=0,
a(m,n)=a(m-1,a(m,n-1)), если m=1, 2, 3, …, n=1, 2, 3, … .

8.9.Программа “обращает” символьную последовательность ( “abcd”→”dcba”


).

8.10. Используйте функцию f(“abcd”)=strcat( f( “bcd” ), ”a” ).

8.11. Программа читает два целых числа m, n и вычисляет сумму


четных чисел из отрезка [ min(m,n]; max(m,n) ].

8.12. Используйте рекурсивную функцию для вычисления суммы!

8.13. Программа читает числовую последовательность и упорядочивает


ее в возрастающем порядке.

8.14. Используйте рекурсивную функцию для упорядочения


последовательности.

8.15. Программа решает задачу Ханойских башень.


Используйте рекурсивную функцию!

Задача Ханойских башень. Пусть заданы три стержня А, В, С,


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

Контрольные вопросы:

8.0.Что такое рекурсивная функция?

74
8.1. Каковы преимущества и недостатки использования рекурсивных
функций?
8.2. Чем отличается определение в программе рекурсивной функции от
определения функции, не являющейся рекурсивной?
8.3. Где, когда и как производится размещение в памяти формальных
параметров и локальных переменных для рекурсивной функции? Объясните
на примере.
8.4. Рассмотрите вопросы теста тест_08 и выберите правильные ответы на
каждый вопрос.

75
Лабораторная работа N 9

Тема: Сортировка данных

Цель работы: Освоение различных методов сортировки данных

Указания:

Пусть задано множество объектов одинакого типа (числа, символы, слова,


структуры данных и т.д.). Под сортировкой данных понимается их
расположение в определенном порядке. Каждый объект содержит одно или
более данных, по которым делается сортировка. Данное, по которому
делается сортировка, называется ключем сортировки.
Сортировку можно выполнить двумя способами:
• переставляя сортируемые элементы так, чтобы их ключи удовлетворяли
порядку сортировки;
• рассмотривая массив поинтеров, указывающих на сортируемые данные,
и сортируя поинтеры, а не данные. Поинтеры сортируются так, чтобы
данные, на которые они указывают в порядке возрастания своих индексов,
были расположены в требуемом порядке сортировки.

Метод пузырьков (bubblesort). Сравниваются первые два элемента


сортируемого массива. Если эти элементы не удовлетворяют требованию
сортировки, тогда они переставляются местами. Потом сравниваются второй
и третий элементы и, если необходимо, они переставляются местами и т.д. до
прохождения всего массива. После этого процесс такой обработки массива
повторяется. Этот процесс прервется тогда, когда все соседние элементы
массива будут удовлетворять требованию порядка сортировки.
Метод пузырьков реализован в примерах 9.1, 9.2.

Метод Shell (shellsort; сортировка с уменьшением инкремента). По этому


методу сравниваются элементы, расстояние между которыми больше 1, в
отличие от метода пузырьков. Если сравниваемые элементы не находятся в
нужном порядке, тогда они переставляются местами. Расстояние между
сравниваемыми элементами называется инкрементом. Инкремент
уменьшается после одного прохождения массива и обработка массива
начинается сначала. Поэтому, такой метод называется еще и методом
сортировки с уменьшением инкремента.
Метод Shell эффективнее метода пузырьков, особенно при сортировке
больших массивов данных.
Эффективность метода зависит от того как выбирается инкремент.
Распространенным методом является выбор инкремента равного половине
количества элементов сортируемого массива. После каждого прохождения
массива, инкремент уменьщается в два раза.
Метод Shell реализован в примере 9.3.

76
Быстрая сортировка I (quicksort; сортировка с взаимообменом позиций).
Выбирается произвольный элемент массива и переставляются элементы в
массиве так, чтобы все элементы, меньшие или равные выбранному, были
слева от него, а все элементы, большие или равные выбранному, были справа
от него. Таким образом исходный массив разбивается на два массива.
Элементы первого массива меньше или равны элементам второго массива.
Далее с каждым из этих двух массивов поступают таким же образом. Такое
разбиение продолжается до тех пор, пока в каждой части не будет только по
одному элементу. В результате исходный массив будет отсортирован по
возрастанию.
Эффективность метода зависит от разделяющего элемента, выбираемого
на каждом шаге. Простым и довольно эффективным методом выбора
разделяющего элемента является выбор элемента из середины массива, в
качестве разделяющего. Назовем метод сортировки с таким способом выбора
разделяющего элемента быстрой сортировкой I (quicksort). Он реализован
в примере 9.4.

Быстрая сортировка II (quicksortinf; сортировка с выбором первого


элемента в качестве разделяющего). Как и в предыдущем методе, исходный
массив разбивается на два массива. Но здесь, в качестве разделяющего
выбирается первый элемент массива и этот элемент перемещается на свое
окончательное место. Слева от него помещаются все элементы меньшие него,
а справа – все элементы большие него. Таким образом, исходный массив
разбивается на две части, а разделяющий элемент не принадлежит ни одной
из них и находится на своем окончательном месте. Далее разделяются по
отдельности полученные два массива тем же методом. Такое разделение
продолжается до тех пор, пока в каждой части не будет только по одному
элементу. В результате исходный массив будет отсортирован по возрастанию.
Назовем метод сортировки с таким способом выбора разделяющего элемента
быстрой сортировкой II (quicksortinf). Он реализован в примере 9.5.

Примеры программ:

Пример 9.0. Функция читает текст, составленный из слов. Пробелы и


символы, не являющиеся буквами, игнорируются. Символ EOF означает
конец вводимого текста. Функция возвращает количество прочтенных слов.
Она сохранена в файле f_9_00.c.

// Exemplul 9_0. Functia citeste un text


int ct(char w[], char *p[]) {
// w[] - Textul, cuvintele sunt separate cu \0
// p[] - Pointerii spre cuvinte
int i=0; // Numărul de cuvinte
int j=0; // Indice pentru cuvinte
char c;
c=getchar();
while( c!=EOF ) {
while( c<'A' || c>'Z' && c<'a' || c>'z' ) {
c=getchar(); if( c==EOF ) break; }
77
if( c==EOF ) break;
p[i++]=w+j;
while( c>='A' && c<='Z' || c>='a' && c<='z' ) {
w[j++]=c; c=getchar(); }
w[j++]='\0'; } return i; }

Пример 9.1. Программа читает числовую последовательность и сортирует


ее, используя метод пузырьков.

// Exemplul 9_1. Metoda bulelor


#include <stdio.h>
#include <conio.h>
void main(void) {
int x[123], i=0,q=1,p=0,n,t; clrscr();
printf("Introdu un şir de întregi şi ^z la sfârşit\n\n");
while( (scanf("%d",x+i)==1) ) ++i;
n=i; clrscr();
printf("Sortare prin metoda bulelor\n\n");
printf("\nŞirul introdus:\n\n");
for(i=0; i<n; i++)
printf(" %d ", *(x+i));
while(q) { q=0;
for(i=0; i<n-1; i++) {
if( *(x+i) > *(x+i+1) ) {
t=*(x+i); *(x+i)=*(x+i+1);
*(x+i+1)=t; q=1; ++p; } } }
printf("\n\nŞirul sortat:\n\n");
for(i=0; i<n; i++)
printf(" %d ", *(x+i));
printf("\n\n%d permutări\n",p);
getch(); }

Пример 9.2. Программа читает текст (последовательность слов) и


сортирует его в возрастающем порядке слов, используя метод пузырьков.
Переставляются местами поинтеры к словам, а не слова, не делается
различия между прописными и заглавными буквами. Текст читается с
помощью функции f_9_00.c из примера 9.0.

// Exemplul 9_2. Metoda bulelor


#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "d:\borlandc\bin\euue\lab09\exemple\f_9_00.c" // Citeste textul
#define lc 20// Lungimea maximă a unui cuvânt
#define nc 50// Numărul maxim de cuvinte
void main(void) {
char w[(lc+1)*nc]; // Textul, cuvintele sunt separate cu \0
char *p[nc], *t;// Pointerii spre cuvinte
int c,i,j,k,q,z;
z=0; // Numărul de permutări
78
i=0; // Numărul de cuvinte
j=0; // Indice pentru cuvinte
clrscr();
i=ct(w,p); // Citeşte textul
clrscr();
printf("Sortare prin metoda bulelor\n\n");
printf("Textul introdus:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
if( i ) { q=1;
while( q ) { q=0;
for(k=0; k<i-1; k++) {
if( stricmp( p[k],p[k+1] ) > 0 ) {
t=p[k]; p[k]=p[k+1];
p[k+1]=t; q=1; ++z; } } }
printf("\nTextul sortat:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]); }
printf("\n\n%d permutări",z);
getch(); }

Пример 9.3. Программа читает (используя функцию f_9_00.c из


примера 9.0) текст и сортирует прочтенные слова в возрастающем порядке,
используя метод Shell. Начальный инкремент есть половина количества слов
текста.

// Exemplul 9_3. Metoda Shell


#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "d:\borlandc\bin\euue\lab09\exemple\f_9_00.c" // Citeste textul
#define lc 20 // Lungimea maximă a unui cuvânt
#define nc 50 // Numărul maxim de cuvinte
int shellsort(char * p[],int n) {
int i,j,z=0,inc; char *t;
for(inc=n/2; inc>0; inc=inc/2) {
for(i=inc; i<n; i++) {
for(j=i-inc; j>=0&&stricmp( p[j],p[j+inc] ) > 0; j=j-inc) {
t=p[j]; p[j]=p[j+inc];
p[j+inc]=t; ++z; } } }
return z; }
void main(void) {
char w[(lc+1)*nc]; // Textul, cuvintele sunt separate cu \0
char *p[nc]; // Pointerii spre cuvinte
char *t;
int c,i,j,k,q,z;
clrscr();
i=ct(w,p); // Citeşte textul
clrscr();
printf("Sortare prin metoda Shell\n\n");
79
printf("Textul introdus:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
z=shellsort(p,i); // Sortează pointerii
printf("\nTextul sortat:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
printf("\n%d permutări",z);
getch(); }

Пример 9.4. Программа читает (используя функцию f_9_00.c из


примера 9.0) текст и сортирует прочтенные слова в возрастающем порядке,
используя быструю сортировку quicksort. Разделяющий элемент есть элемент
из середины массива.

// Exemplul 9_4. Sortare rapidă I (quicksort)


// Elementul separator este elementul din mijlocul tabloului
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "d:\borlandc\bin\euue\lab09\exemple\f_9_00.c" // Citeste textul
#define lc 20 // Lungimea maximă a unui cuvânt
#define nc 50
int z=0; // Numărul maxim de cuvinte
int quicksort(char * p[],int inf, int sup) {
int i,j; char *x, *t;
i=inf; j=sup; x=p[(i+j)/2];
do {
while( i<sup&&stricmp(p[i],x)<0 ) i++;
while( j>inf&&stricmp(p[j],x)>0 ) j--;
if( i<=j )
{ if( i<j )
{ t=p[i]; p[i]=p[j]; p[j]=t; ++z; }
i++; j--; }
}while( i<=j );
if( inf<j ) quicksort(p,inf,j);
if( i<sup ) quicksort(p,i,sup);
return z; }
void main(void) {
char w[(lc+1)*nc]; // Textul, cuvintele sunt separate cu \0
char *p[nc]; // Pointerii spre cuvinte
int c,i,j;
clrscr();
i=ct(w,p); // Citeşte textul
clrscr();
printf("Sortare prin metoda rapidă I\n\n");
printf("Textul introdus:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
z=quicksort(p,0,i-1); // Sorteaza pointerii
80
printf("\nTextul sortat:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
printf("\n%d permutări",z);
getch(); }

Пример 9.5. Программа читает (используя функцию f_9_00.c из


примера 9.0) текст и сортирует прочтенные слова в возрастающем порядке,
используя быструю сортировку quicksortinf. Разделяющий элемент есть
элемент из середины массива.

// Exemplul 9_5. Sortare rapidă II (quicksortinf)


// Elementul separator este primul element al tabloului
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "d:\borlandc\bin\euue\lab09\exemple\f_9_00.c" // Citeste textul
#define lc 20 // Lungimea maximă a unui cuvânt
#define nc 50
int z=0; // Numărul maxim de cuvinte
int quicksortinf(char * p[],int inf, int sup) {
int i,j; char *x, *t;
i=inf; j=sup; x=p[inf];
while( i<j ) {
while( i<sup&&stricmp(p[i],x)<=0 ) i++;
while( stricmp(p[j],x)>0 ) j--;
if( i<j )
{ t=p[i]; p[i]=p[j]; p[j]=t; ++z; } }
if( j>inf )
{ p[inf]=p[j]; p[j]=x; ++z; }
if( inf<j-1 ) quicksortinf(p,inf,j-1);
if( j+1<sup ) quicksortinf(p,j+1,sup);
return z; }
void main(void) {
char w[(lc+1)*nc]; // Textul, cuvintele sunt separate cu \0
char *p[nc]; // Pointerii spre cuvinte
int c,i,j;
clrscr();
i=ct(w,p); // Citeşte textul
clrscr();
printf("Sortare prin metoda rapidă II\n\n");
printf("Textul introdus:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
z=quicksortinf(p,0,i-1); // Sortează pointerii
printf("\nTextul sortat:\n\n");
for(j=0; j<i; j++)
printf("%s\n",p[j]);
printf("\n%d permutări",z);
getch(); }
81
Содержание работы:

Составьте программы для нижеследующих заданий. Сохраните программы


на сервере и запишите в тетрадь.

9.0. Наберите и запустите программы примеров 9.1-9.5.

9.1. В методе пузырьков, после первого прохождения массива, элемент,


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

9.2. Составьте программу для сортировки по убыванию слов вводимого


текста методом пузырьков с учетом замечания из задания 9.1. Вместе с
отсортированным массивом слов выведите и количество выполненных
перестановок.

9.3. Составьте программу для сортировки по убыванию числовой


последовательности методом Shell. Вместе с отсортированным массивом
чисел выведите и количество выполненных перестановок.

9.4. Составьте программу для сортировки по убыванию числовой


последовательности методом быстрой сортировки I. Вместе с
отсортированным массивом чисел выведите и количество выполненных
перестановок.

9.5. Составьте программу для сортировки по убыванию числовой


последовательности методом быстрой сортировки II. Вместе с
отсортированным массивом чисел выведите и количество выполненных
перестановок.

9.6. Составьте программу которая читает числовую последовательность и


сортирует ее по убыванию методами: пузырьков, Shell, быстрой
сортировки I, быстрой сортировки II. Вместе с отсортированным массивом
чисел выведите и количество выполненных перестановок. Сравните
количества выполненных перестановок различными методами.

9.7. Модифицируйте метод быстрой сортировки quicksort и программу 9.4


так, чтобы разделяющим элементом была медиана первого, последнего и
серединного элементов массива. (Медиана чисел a, b, c, для которых a<b<c,
есть число b).
82
9.8. Модифицируйте метод быстрой сортировки quicksortinf и программу
9.5 так, чтобы разделяющим элементом был последний элемент массива.

Контрольные вопросы:

9.1. Что понимается под сортировкой данных и какие способы


сортировки вы знаете?
9.2. Объясните идею метода пузырьков для сортировки данных.
9.3. Объясните идею метода Shell для сортировки данных.
9.4. Объясните идею I метода быстрой сортировки данных.
9.5. Объясните идею II метода быстрой сортировки данных.
9.6. Что такое ключ сортировки?
9.7. Что является ключем сортировки в примерах 9.1-9.5?
9.8. Что является ключем сортировки в задачах 9.1-9.5?
9.9. Что произойдет, если при сортировки методом Shell количество
элементов исходной последовательнсти будет степенью 2?
9.10.Что произойдет, если, при применении метода быстрой
сортировки, разделяющим элементом будет максимальный или
минимальный элемент массива?
9.11. Что произойдет при применении метода быстрой сортировки I
или II, если все элементы массива равны между собой? Модифицируйте
программы 9.4 и 9.5 так, чтобы не делать ненужных перестановок.
9.12.Рассмотрите вопросы теста тест_09 и выберите правильные
ответы на каждый вопрос.

83
П р и л о ж е н и е 1

Спецификаторы формата

При выводе данных ( функция printf() ), спецификатор формата указывает


как нужно конвертировать данные из внутреннего формата в внешний
формат, а при вводе данных ( функция scanf() ), спецификатор формата
указывает как нужно преобразовывать данные из внешнего формата в
внутренний формат.

Спецификатор формата имеет форму: %[flags][width][.prec][dim]type


В следующей таблице описываются элементы спецификатора формата.

Элемент Символ Что означает


- Выравнивает числа по левому краю поля вывода.
0 Заполняет неиспользованные позиции поля вывода
цифрой 0, а не пробелом.
+ Всегда выводит знак ( + или - ) для числовых значений.
пробел Выводит пробел на место знака ( + ) для числовых
flag
значений.
# Выводит цифру 0 перед восьмиричными числами и 0x
или 0X перед шестнадцатиричными числами. Его
присутствие перед буквами g, G, f, e, E определяет
вывод десятичной точки даже если значение целое.
Ряд десятичных Определяет ширину поля ввода или вывода.
width
цифр
Ряд десятичных Для символьных последовательностей - максимальное
цифр количество выводимых символов; для целых чисел -
prec
минимальное количество выводимых цифр; для
дробных чисел - количество цифр после точки.
F Аргументом является поинтер типа far.
N Аргументом является поинтер типа near.
h ( перед d,i,o,u,x,X ) Аргумент имеет тип short int.
dim
l ( перед d,i,o,u,x,X) Аргумент имеет тип long int.
l ( перед f,e,E,g,G ) Аргумент имеет тип double.
L ( перед f,e,E,g,G ) Аргумент имеет тип long double.
d Аргумент имеет тип signed int.
i Как и d.
o Аргументом является восьмиричное число без знака.
u Аргумент имеет тип unsigned unt.
x Аргументом является шестнадцатиричное число без
знака.
X Как и x только в верхнем регистре.
f Число с плавающей точкой в форме [-]m.n, где m целая
type часть, а n дробная часть.
e Число с плавающей точкой в форме [-]m.ne[+/-]d. При
выводе число нормализовано и буква e прописная.
g Как f или e ( с минимальным количеством позиций )
E Как e, только буква E заглавная.
G Как f или E.
c Аргументом является символ ( тип char ).
s Аргументом является символьная последовательность.
p Аргументом является поинтер.

84
B I B L I O G R A F I E

1. Dr. Kris Jamsa, Lars Klander. Totul despre C şi C++. Manual fundamental
de programare în C şi C++. Bucureşti: Teora, 2000.
2. Herbert Shildt. C manual complet. Editura Teora, Bucureşti, 1998.
3. Liviu Negrescu. Limbajele C şi C++ pentru începători. V. 1. Editura
Microinformatica, Cluj-Napoca, 1995.
4. Vasile Petrovici, Florin Goicea. Programarea în limbajul C. Editura Tehnică,
Bucureşti, 1993.
5. Ştefan BERZAN, Rodica BERZAN. Programarea calculatoarelor. Teste,
probleme, coduri C pentru lucrările practice şi de laborator. Editura ASEM,
Chişinău, 2003.
6. Герберт Шилдт. Полный справочник по С. Москва:Издательский дом
“Вильямс”, 2002.
7. Ştefan BERZAN, Rodica BERZAN. Программирование. Tесты, задачи,
коды С для практических и лабораторных работ. Editura ASEM, Chişinău,
2003.
8. Культин Н.Б. С/С++ в задачах и примерах. СПб.: БХВ-Петербург, 2001.
9. Бьерн Строустран. Язык программирования С++.
10. Arnush Craig. Borland C++. Moscova, 1997.
11. http://www.brainbench.com
12. http://www.cplus.about.com
13. http://www.programare.ro
14. http://www.specialist.ru
15. http://www.ipg.h1.ru
16. http://www.citforum.ru

85

Вам также может понравиться