
- •Отображает данные, вводимые в ручную, во время обработки с устройств любого типа (клавиатура, переключатели, кнопки, световое перо, полоски со штрих кодом и т.д.).
- •Символ отображает хранимые данные в виде, пригодном для обработки. Носитель данных не определен. В схемах алгоритмов он предназначен для обозначения ввода-вывода данных в случае использования запоминающего устройства, управляемого процесса.
- •Тема 1. Основные этапы решения задач на ЭВМ
- •Постановка задачи разработки программного обеспечения
- •Анализ формальной постановки задачи
- •Выбор или разработка математической модели и метода решения
- •Разработка алгоритма
- •Базовые структуры алгоритма
- •Тема 2. Жизненный цикл программы. Критерии качества программы.
- •Техническое задание и спецификация программы
- •Разработка проекта программной системы
- •Программирование (кодирование) или программная реализация алгоритмов
- •Тестирование и отладка
- •Эксплуатация и сопровождение
- •Критерии качества программного обеспечения
- •Тема 3. Схемы алгоритмов, данных, программ
- •Символы данных
- •Символы процесса
- •Символы линий
- •Специальные символы
- •Правила применения символов в схемах
- •Правила выполнения соединений
- •Специальные условные обозначения
- •Тема 4. Язык программирования высокого уровня Си
- •Общие сведения о языке Си
- •Алфавит языка Си
- •Грамматика для описания языка, синтаксические диаграммы
- •Структура программы на языке Си
- •Имена объектов в программе
- •Выражения, операции и приоритеты
- •Тема 5. Стандартные типы данных
- •Тема 6. Составные типы данных
- •Данные регулярного типа (массивы)
- •Строки
- •Данные комбинированного типа (структуры)
- •Перечисления
- •Объединения
- •Указатели
- •Тема 7. Представление основных управляющих структур программирования
- •Оператор присваивания
- •Составной оператор
- •Оператор перехода Goto
- •Условный оператор If
- •Оператор выбора switch
- •Операторы цикла while, do – while, for
- •Операторы прерывания циклов
- •Форматированный ввод данных
- •Форматированный вывод данных
- •Преобразование типов
- •Инициализация данных
- •Тема 8. Функции
- •Определение функций в языке Си
- •Вызов функций в языке Си
- •Рекурсивные функции
- •Тема 9. Файлы
- •Тема 10. Приемы программирования. Примеры алгоритмов
- •Алгоритмы сортировки
- •Алгоритмы поиска
- •Динамические структуры данных
- •Линейные списки
- •Стек, очередь, дек
- •Деревья
- •Приложение 1. Стандартные библиотеки языка Си
- •Приложение 2. Примеры реализации алгоритмов
- •Не рекурсивный алгоритм решения задачи Ханойская башня.
- •Рекурсивный алгоритм решения задачи Ханойская башня.
- •Приложение 3. Лабораторные работы
- •Лабораторная работа №1
- •Лабораторная работа №2
- •Лабораторная работа №3
- •Лабораторная работа №4
- •Лабораторная работа №5
- •Лабораторная работа №6
- •Лабораторная работа №7
- •Лабораторная работа №8
- •Лабораторная работа №9
- •Лабораторная работа №10
- •Лабораторная работа №11
- •Лабораторная работа №12
- •Список литературы
Здесь «Цифра1» – цифры целой части мантиссы; «Цифра2» – цифры дробной части мантиссы; Е – признак десятичного порядка; «Цифра3» – цифры десятичного порядка.
Приведем примеры вещественных чисел с десятичным порядком:
1.Вещественное число 45,3·10–3 может представляться на языке Си в виде вещественной константы с десятичным порядком следующим образом: +45.3E– 3 или 4.53E–2 или 0.453E–1.
2.Вещественное число –45·104 может быть представлено на языке Си в виде вещественной константы с десятичным порядком следующим образом: –45E4 или –4.5E5 или –0.45E+6.
Количество значащих цифр в вещественной константе и величина порядка зависят от разновидности вещественного числа и определяются в соответствии с приведенной ранее таблицей.
Тема 6. Составные типы данных
Данные регулярного типа (массивы)
Массивом называется структура данных, позволяющая хранить под одним именем совокупность (последовательность) данных (переменных) любого, но только одного какого–то типа. Массив характеризуется размерностью, своим именем, базовым типом хранимых элементов, размером и способом нумерации элементов.
Имя массива – общее имя переменных, входящих в массив. Базовый тип массива – тип переменных, входящих в массив. Элементы массива – переменные, входящие в массив.
В тексте программы элементы массива изображаются в виде переменных с индексами. Индексы записываются в квадратных скобках сразу после имени массива и разделяются между собой запятыми. Имя массива совпадает с именем переменной с индексами, т.е. с именем элемента массива. Конкретные значения
68

индексов определяют положение элемента в массиве и однозначно идентифицируют его.
Размерность массива – количество индексов, которое необходимо для однозначной идентификации любого элемента массива.
Размер массива – общее количество элементов в массиве.
На практике довольно часто используют одномерные и двухмерные массивы. Одномерный массив иногда называют вектором, двумерный – матрицей. Одномерный массив удобно представлять в виде группы последовательных ячеек памяти, а матрицу – в виде квадратной таблицы.
Статические массивы можно объявлять с инициализацией, перечисляя значения их элементов в {} через запятую. Если задано меньше элементов, чем длина массива остальные элементы считаются нулями:
int a10[10] = { 1, 2, 3, 4 }; /* и 6 нулей */
Если при описании массива с инициализацией не указать его размер, он будет подсчитан компилятором:
int a3[] = { 1, 2, 3 }; /* как бы a3[3] */
Массивы определяются с помощью синтаксической конструкции, показанной на рис.27.
Тип данных |
Имя |
[ |
Количество |
] |
массива |
элементов |
Рис. 27. СД описания массивов в языке Си
Тип элемента может быть любым допустимым типом языка Си (в том числе и регулярным типом (массивом) тоже), а в качестве типа индекса может использоваться любое выражение, выдающее значение целого типа: char, short, int, long. При описании переменной регулярного типа строго фиксируются границы изменения каждого индекса. В дальнейшем эти границы изменять нельзя. Как правило, индексы массива представляют ограниченный тип на базе целого, а верхняя граница диапазона описывается как константа:
int N = 10;
69

int |
M |
= 20; |
|
|
|
|
float |
A [N] [M]; |
|
// |
матрица NxM |
из 200 элементов |
|
int |
B |
[N]; |
// вектор из 10 целых элементов |
|||
char C {'A','F',’6’,’5’} |
// |
вектор из 4 |
символьных элементов |
В разделе операторов для обращения к элементам описанных массивов могут использоваться переменные с индексами, причем в качестве индексов могут выступать любые выражения, выдающие результат такого же типа, что и соответствующий индекс, например:
A[2,3]:=3.14;
A[i,j]:=i*i+j–2;
A[i,2]:=A[i–1,j+2];
B[j]:=3; B[j+i]:=i+j;
Строки
Строка в Си - это последовательность байт (букв, символов, литер, character), завершающаяся в конце специальным признаком - байтом '\0'. Этот признак добавляется компилятором автоматически, когда мы задаем строку в виде "строка". Длина строки (т.е. число литер, предшествующих '\0') нигде явно не хранится. Длина строки ограничена лишь размером массива, в котором сохранена строка, и может изменяться в процессе работы программы в пределах от 0 до длины массива-1. При передаче строки в качестве аргумента в функцию, функции не требуется знать длину строки, т.к. передается указатель на начало массива, а наличие ограничителя '\0' позволяет обнаружить конец строки при ее просмотре. В табл.8 представлены строковые типы данных.
|
|
|
Таблица 8. |
|
|
|
|
Строковые типы данных в языке Си |
|
|
Размер в байтах |
|
Знаковый |
|
Тип |
Диапазон значений |
(может ли хранить |
||
(битах) |
||||
|
|
отрицательные числа) |
||
Char |
|
|
||
1 (8) |
от -128 до 127 |
Да |
||
Unsigned char |
1 (8) |
от 0 до 255 |
Нет |
|
Signed char |
1 (8) |
от -128 до 127 |
Да |
Базовым типом данных является Char. Объект этого базового типа может быть модифицирован с помощью следующих модификаторов: unsigned, signed.
Язык Си позволяет определить строки двумя различными способами. В первом используется массив символов, а во втором - указатель на первый символ массива.
70
Определение char а[10]; указывает компилятору на необходимость резервирования места для максимум 10 символов. Константа а содержит адрес ячейки памяти, в которой помещено значение первого из десяти объектов типа char. Процедуры, связанные с занесением конкретной строки в массив а, копируют ее по одному символу в область памяти, на которую указывает константа а, до тех пор, пока не будет скопирован нулевой символ, оканчивающий строку. Когда выполняется функция типа printf("%s", а), ей передается значение а, т.е. адрес первого символа, на который указывает а. Если первый символ - нулевой, то работа функции printf() заканчивается, а если нет, то она выводит его на экран, прибавляет к адресу единицу и снова начинает проверку на нулевой символ. Такая обработка позволяет снять ограничения на длину строки (конечно, в пределах объявленной размерности): строка может иметь любую длину, но в пределах доступной памяти.
Инициализировать строку при таком способе определения можно следующим образом:
char array[7] = "Строка";
char s[ ] = {'С', 'т', 'р', 'о', 'к', 'а', '\0'};
(при определении массива с одновременной инициализацией пределы изменения индекса можно не указывать).
Второй способ определения строки - это использование указателя на символ. Определение char *b; задает переменную b, которая может содержать адрес некоторого объекта. Однако в данном случае компилятор не резервирует место для хранения символов и не инициализирует переменную b конкретным значением. Когда компилятор встречает оператор вида b ="IBM PC";, он производит следующие действия. Во-первых, как и в предыдущем случае, он создает в каком-либо месте объектного модуля строку "IBM PC", за которой следует нулевой символ ('\0'). Во-вторых, он присваивает значение начального адреса этой строки (адрес символа 'I') переменной b. Функция printf("%s", b) работает так же, как и в предыдущем случае, осуществляя вывод символов до тех пор, пока не встретится заключительный нуль.
71
Массив указателей можно инициализировать, т.е. назначать его элементам конкретные адреса некоторых заданных строк при определении.
Для ввода и вывода строк символов помимо scanf() и printf() могут использоваться функции gets() и puts() (их прототипы находятся в файле stdio.h).
Если string – массив символов, то ввести строку с клавиатуры можно так:
gets(string);
(ввод оканчивается нажатием клавиши <Enter>). Вывести строку на экран можно следующим образом:
puts(string);
Отметим также, что для работы со строками существует специальная библиотека функций, прототипы которых находятся в файле string.h.
Наиболее часто используются функции strcpy(), strcat(), strlen() и strcmp(). srcpy() – используется для копирования строки или ее части в другую
строку.
Копирование происходит побайтно, пока не встретится ‘\0’. Возвращает указатель на строку s1.
strcpy(s1, s2); // копирует строку s2 в строку s1 |
|
strncpy(s1, s2,n); |
//копирует первые n символов из строки s2 в строку s1 |
Помните, что указываемый массив-приемник s1 должен быть достаточного размера, чтобы содержать строку-источник s2, иначе программа может быть испорчена. Если n больше, чем длина строки в s2, то в s1 символы заполняются нулями до величины n.
Задавая аргумент-источник не ссылкой на начало символьного массива, а адресом любого его элемента, мы можем скопировать либо правую, либо среднюю подстроку:
9 |
strcpy(s1,&s2[k]); |
//копирует правую подстроку из s2 в s1 |
9 |
strncpy(s1,&s[2],n); |
//копирует среднюю подстроку из s2 в s1 |
char * strcpy_my (char *s1, char *s2) //Пример функции копирования |
||
{ |
char *ptrs1 = s1; |
|
|
while (( *s1++ = *s2++) != 0); |
|
|
return ptrs1; |
|
}
72
Обратите внимание, что использование нулевого ограничителя упрощает различные операции над строками.
Длина строк в языке си определяется системной функцией strlen(). Единственным ее аргументом ёявляется анализируемая строка. Функция возвращает длину строки в символах без учета нулевого ограничителя.
void main() |
/*пример функции*/ |
|
{ |
char str[80]; |
|
|
printf("ввести строку: "); |
|
|
gets(str); |
|
} |
printf("%d", strlen(s)); |
В Си операция конкатенации (объединения) строк реализуется с помощью функции strcat():
strcat(s1,s2); //добавляет s2 к s1 |
|
strncat(s1,s2,n); |
//добавляет n первых символов из s2 к s1 |
Поиск вхождения одной строки в другую дает ответ на вопрос, содержится ли значение одного текста в другом и с какой позиции обнаружено это вхождение. Нулевая позиция в качестве результата такой операции соответствует отрицательному ответу.
/*Пример собственной функции
73
strcmp() – осуществляет сравнение текстовых данных. Операции сравнения отдельных символов или строк основаны на последовательном анализе отношений числовых значений соответствующих кодов. В кодовых страницах символы букв упорядочены в соответствии их расположением в латинском или национальном алфавитах. Поэтому код буквы "A" меньше кода буквы "F", код буквы "Г" меньше кода буквы "Ю" и т.д.
Некоторое неудобство вызывает тот факт, что одноименные большие и малые буквы имеют разные коды – в одной клетке кодировочной таблицы можно разместить только один символ, кроме того большие и малые буквы имеют разный смысл. Это не позволяет напрямую упорядочить слова в соответствии с их лексикографическим расположением в словарях. Поэтому приходится предварительно заменять коды всех малых букв в тексте на коды больших (или наоборот) и только после этого выполнять операцию сравнения. Такая замена для букв латинского алфавита особых проблем не представляет, т.к. смещение между кодами соответствующих больших и малых букв - величина постоянная. А вот с русскими буквами приходится повозиться – в кодировке ASCII цепочка малых букв между "п" и "р" разорвана символами псевдографики, а буквы "Ё" и "ё" вообще находятся не на своих местах.
Учитывая эту специфику, следует достаточно внимательно использовать языковые средства, связанные с преобразованием или игнорированием разницы в кодировке больших и малых букв. Для русскоязычных текстов их применять нельзя.
Язык Си позволяет преобразовывать содержимое символьных строк к верхнему strupr(s) или к нижнему strlwr(s) регистру. Но коды символов, не принадлежащих множеству букв латинского алфавита, остаются при этом без изменения.
Для сравнения строк Си предлагает довольно много системных функций, но не забывайте, что их действие не всегда допустимо над русскими словами. Каждая из описываемых ниже функций принимает положительное значение, если ее первый операнд строго "больше" (лексикографически) второго, нулевое
74
значение при "равенстве" операндов, и отрицательное значение, если первый операнд оказался "меньше".
strcmp(s1,s2);//сравнивает строки s1 и s2 |
|
strcmpi(s1,s2); //сравнивает s1 и s2 с игнорированием разницы между |
|
//большими и малыми буквами |
|
stricmp(s1,s2); |
//эквивалентна функции strcmpi |
strncmp(s1,s2,k); |
//сравнивает первые k символов в s1 и s2 |
strncmpi(s1,s2,k); |
//сравнивает первые k символов в s1 и s2 с |
//игнорированием разницы между большими и |
|
//малыми буквами |
|
strnicmp(s1,s2,k); |
//эквивалентна функции strncmpi |
Функцию strcmp() можно использовать для проверки вводимого пароля, как показано в следующем примере:
/* вернуть "верно", если пароль угадан */ password()
{char s[80];
printf("ввести пароль: "); gets(s);
if(strcmp(s, "пароль"))
{printf("пароль ошибочен\n"); return 0;
}
}
return 1;
Имейте в виду, что если строки равны, функция strcmp() возвращает "ложь", и если вы хотите использовать это условия для другого действия, необходимо записывать оператор логического отрицания «!» (NOT), как показано в следующем примере:
main()
{ char s[80]; for('')
{ printf(": "); gets(s);
} }
if(!strсmp("quit", s)) break;
Эта программа будет продолжать запрашивать ввод до тех пор, пока не будет введено слово quit. Следующий пример иллюстрирует действие функций обработки строк:
main()
{char s1[80], s2[80]; gets(s1); gets(s2);
printf("Длина: %c %c\n", strlen(s1), strlen(s2));
75