ÌÈÍÈÑÒÅÐÑÒÂÎ ÎÁÐÀÇÎÂÀÍÈß ÐÎÑÑÈÉÑÊÎÉ ÔÅÄÅÐÀÖÈÈ
Ñàíêò-Ïåòåðáóðãñêèé ãîñóäàðñòâåííûé óíèâåðñèòåò àýðîêîñìè÷åñêîãî ïðèáîðîñ...
4 downloads
523 Views
321KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
ÌÈÍÈÑÒÅÐÑÒÂÎ ÎÁÐÀÇÎÂÀÍÈß ÐÎÑÑÈÉÑÊÎÉ ÔÅÄÅÐÀÖÈÈ
Ñàíêò-Ïåòåðáóðãñêèé ãîñóäàðñòâåííûé óíèâåðñèòåò àýðîêîñìè÷åñêîãî ïðèáîðîñòðîåíèÿ
Н.В. Кучин, М.М.Павлова
ОСНОВЫ ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ СИ Учебное пособие
Ñàíêò-Ïåòåðáóðã 2001
УДК 519. 682 (075) ББК 32. 973 – 18.1 К95 Кучин Н. В., Павлова М. М. К95 Основы программирования на языке СИ: Учеб. пособие / СПбГУАП. СПб., 2001. 86 с. Изложены и систематизированы базовые элементы языка СИ. Рассмотрены конструкции языка СИ в объеме, позволяющем составлять программы, как для простых, так и достаточно сложных задач. Приведены примеры для отработки навыков составления программ. Пособие предназначено для студентов специальности «Вычислительные машины, комплексы, системы и сети», направления «Информатика и вычислительная техника», кроме того может быть использовано для студентов всех специальностей университета, изучающих курсы «Информатика» и «Программирование»
Рецензенты: кафедра информатики и прикладной математики СПбГИТМО (ТУ) (кандидат технических наук доцент Т. А. Павловская); профессор кафедры радиолокационных систем государственного университета телекоммуникаций им. М. А. Бонч-Бруевича доктор технических наук В. В. Волков
Óòâåðæäåíî ðåäàêöèîííî-èçäàòåëüñêèì ñîâåòîì óíèâåðñèòåòà â êà÷åñòâå ó÷åáíîãî ïîñîáèÿ
© СПбГУАП, 2001 © 2
Н.В.Кучин, М.М.Павлова, 2001
ПРЕДИСЛОВИЕ Язык C был создан в начале 70-х годов двадцатого столетия и в настоящее время является основой для создания значительной части системных программ и приложений. Он эффективен как для решения задач системного программирования, так и для создания прикладных программ. Среди преимуществ языка C можно отметить переносимость программ, написанных на языке С, на компьютеры различной архитектуры и из одной операционной системы в другую, лаконичность записи алгоритмов, логическую стройность, возможность получить эффективный код программ, сравнимых по скорости с программами, написанными на ассемблере. Удобство языка С основано на том, что он является одновременно и языком высокого уровня, имеющим полный набор конструкций структурного программирования, поддерживающим модульность, блочную структуру программ, возможность раздельной компиляции модулей. В то же самое время язык С имеет набор низкоуровневых средств, позволяющих иметь удобный доступ к аппаратным средствам компьютера, в частности, позволяющих добраться до каждого бита памяти. Гибкость и универсальность языка С обеспечивает его широкое распространение. Учебное пособие ориентировано на начинающих программистов, имеющих первоначальные понятия об основах алгоритмизации на уровне курса информатики. Каждый раздел пособия снабжен соответствующими примерами, которые представляют собой программы, написанные на языке С, или отдельные конструкции таких программ. Все приводимые примеры используют стандарт ANSI языка С и ориентируются на трансляторы, созданные фирмой Borland International Inc., как самые распространенные. Усвоение учебного материала, представленного в данном пособии, позволит приобрести устойчивые навыки правильного написания программ на языке С, а также значительно облегчить изучение объектно-ориентированного языка – С++.
3
1. ОСНОВНЫЕ ПОНЯТИЯ ЯЗЫКА С 1.1. Алфавит, идентификаторы, ключевые слова, комментарии Алфавитом языка называется совокупность символов, используемых в языке. Язык C различают, в отличие от многих других языков программирования, прописные и строчные буквы. Язык C, как говорят, является чувствительным к регистру. В языке С имена COLOR, color, и Color определяют три различные имени переменных. Поэтому при написании программ необходимо быть внимательным при использовании регистров. Обычно используется следующее соглашение – имена переменных содержат только строчные буквы (нижний регистр), константы и макросы – прописные буквы (верхний регистр). В именах переменных можно использовать символ подчеркивания. Обычно с символа подчеркивания начинаются имена системных переменных и констант. В библиотечных функциях также часто используются имена, начинающиеся с символа подчеркивания. Поэтому лучше не использовать этот символ как первый при написании своих собственных переменных, что позволит избежать возможных конфликтов. Идентификаторы в языке программирования используются для обозначения имен переменных, функций и меток, применяемых в программе. Идентификатором может быть произвольная последовательность латинских букв (прописных и строчных), цифр и символа подчеркивания, которая начинается с буквы или символа подчеркивания. В языке C идентификатор может состоять из произвольного числа символов, однако два идентификатора считаются различными, если у них различаются первые 32 символа. В языке C некоторые идентификаторы употребляются как служебные слова, которые имеют специальное значение для компилятора. Их употребление строго определено, и эти слова не могут использоваться иначе. Ключевыми словами стандарта ANSI языка C являются 4
ayto break case char const continue default do
double else enum extern float for goto if
int long registr return short signed sizeof static
struct switch typedef union unsigned void volatile while
Часть символов язык C рассматривает как пробельные символы. Это не только символ пробела ‘ ‘, но символы табуляции, символы перевода строки, возврата каретки, символ перевода страницы. Комментарий – это часть программы, которая игнорируется компилятором и служит для удобочитаемости текста программы. В процессе компиляции комментарий заменяется пробелом, следовательно, комментарий может располагаться в любом месте программы, где допустимо использование пробела. Комментарием в языке C является любая последовательность символов, заключенная между парой символов /* и */. 1.2. Примеры простых программ Рассмотрим следующую программу: # include < stdio.h > /* Пример 1 */ main() { int year; year = 2000; printf(“Сейчас %d год\n”,year); } Первая строка # include < stdio.h > сообщает о необходимости подключения файла stdio.h. Этот файл содержит информацию, необходимую для правильного выполнения функций библиотеки ввода/вывода языка С. Язык C предусматривает использование таких файлов, которые называются заголовочными. В данном случае использование файла stdio.h необходимо, так как в этом файле находится информация о функции printf () , которая используется в нашей программе. Вторая строка /* Пример 1 */ является комментарием. 5
Строка main() определяет имя функции. Любая программа на языке C включает одну или более функций. Выполнение программы начинается с функции main(). Поэтому каждая программа на языке C должна содержать эту функцию. Следующая строка содержит открывающуюся фигурную скобку, обозначающую начало тела функции main(). Фигурные скобки в языке C всегда используются парами (открывающая и закрывающая). Закрывающая скобка встретится в данной программе далее. Строка int year; объявляет переменную, названную year, и сообщает компилятору, что это переменная целого типа. В языке C все переменные должны быть объявлены прежде, чем они будут использованы. Процесс объявления переменных включает в себя определение имени и указания типа переменных. Строка year = 2000; является оператором присваивания. Здесь переменной с именем year присваивается значение 2000. Заметим, что в языке C в операторе присваивания используется просто знак равенства. Все операторы в языке C заканчиваются символом “ точка с запятой”. Строка printf(“Сейчас %d год\n”,year); является вызовом стандартной функции printf(), которая выводит на экран некоторую информацию. Эта строка состоит из двух частей: имени функции printf() и двух ее аргументов “Сейчас %d год\n” и year, разделенных запятой. Функция printf() является универсальной функцией форматного вывода. Для вызова функции нужно правильно написать имя функции и в скобках указать необходимые фактические аргументы. Первый аргумент функции printf() – это строка в кавычках “Сейчас %d год\n”, которую называют управляющей строкой. Эта строка может содержать любые символы или спецификации формата, начинающиеся с символа %. Обычные символы просто отображаются на экран в том порядке, в котором они следуют. Спецификация формата, начинающаяся с символа %d указывает формат, в котором будет выводится значение переменной year, являющейся вторым аргументом функции printf(). Спецификация %d указывает, что будет выводится целое число в десятичной записи. Комбинация символов ‘\n’ сообщает функции printf() о необходимости перехода на новую строку. Этот символ называется символом новой строки. Последняя строка программы содержит фигурную закрывающую скобку. Она обозначает конец функции main(). 6
Рассмотрим второй пример, в котором будет использоваться ввод данных с клавиатуры. Для этого будет использоваться библиотечная функция scanf(), которая позволяет вводить информацию с клавиатуры во время выполнения программы # include < stdio.h > /* Пример 2: вычисление длины окружности */ main() { int radius; float length; printf(“ Введите значение радиуса: \n”); scanf(“%d”,&radius); length = 3.1415*2*radius; printf(“Радиус-%d\n,Длина- %f\n”,radius,length); } В этой программе по сравнению с предыдущей появились новые моменты. Во-первых, объявлены переменные двух разных типов: radius – типа целое(int); length – типа с плавающей запятой (float), содержащую дробную часть. Во-вторых, используется функция scanf() для ввода с клавиатуры значения радиуса окружности. Первый аргумент функции scanf() “%d” указывает, что будет вводится целое десятичное число. Второй аргумент – имя переменной, которой будет присвоено введенное значение. Символ & (амперсанд) перед именем переменной radius необходим для правильной работы функции scanf(). Более подробно использование этого символа будет обсуждаться позднее. В следующей строке программы целые числа 2 и radius умножаются на число с плавающей запятой 3.1415 и результат присваивается переменной типа float. Язык C допускает использование в выражениях переменных разных типов. Для вывода результатов используется функция printf(), которая содержит 4 аргумента. Спецификатор формата %f используется для печати значения переменной length типа float. В рассмотренном примере длина окружности вычисляется только для целых радиусов. Для того чтобы программа могла вычислять длину окружности для любых радиусов, необходимо объявить переменную radius как float, а в функции scanf() использовать спецификатор “%f”. 7
1.3. Определение некоторых понятий Исходный текст (source cod) – текст программы на языке программирования. Объектный код (object cod) – текст программы на машинном языке, который не может выполняться компьютером. Получается после компиляции исходного текста файла или программы. Компоновщик (linker) – программа, строящая выполняемый модуль из объектных модулей. Эта программа собирает откомпилированный текст программы и функций из стандартных библиотек языка C в одну выполняемую программу. Библиотека (library) – набор функций, включая стандартные, предопределенных переменных и констант, которые могут быть использованы в программе и хранятся в откомпилированном виде. Время компиляции (compiler time) – период, во время которого происходит компиляция программы. Во время компиляции обнаруживаются синтаксические ошибки, сделанные при составлении текста программы. Время выполнения (run time) – период, во время которого происходит выполнение программы.
8
2. ПЕРЕМЕННЫЕ, КОНСТАНТЫ, ОПЕРАЦИИ И ВЫРАЖЕНИЯ 2.1. Базовые типы данных и объявление переменных В языке C все переменные должны быть объявлены до их использования. В нем определены 5 типов данных, которые считаются базовыми: char – символьные; int – целые; float – с плавающей точкой; double – с плавающей точкой двойной длины; void – пустой, не имеющий значения. Типы char и int являются целыми типами и предназначены для хранения целых чисел. Хотя тип char по названию символьная переменная, любой символ в компьютере связан с целым числом – кодом этого символа в таблице ASCII. Сам символ нам необходим, когда информация выводится на экран или на принтер или, наоборот, вводится с клавиатуры. Подобные преобразования символа в код и обратно производятся автоматически. Тип char по умолчанию является знаковым типом, однако, настройкой опций интегрированной среды можно установить по умолчанию беззнаковый тип char. Тип int является всегда знаковым, также как и типы float и double. Переменные типа double и float являются числами с плавающей точкой. Ключевое слово void привнесено из стандарта ANSI для языка C++. Нельзя создать переменную типа void. Тем не менее введение такого типа оказалось необходимым, что будет показано в дальнейшем. На основе этих пяти типов строятся другие типы данных. Простейшим приемом построения других типов является использование модификаторов, которые ставятся перед соответствующим типом. В стандарте ANSI языка C такими модификаторами являются следующие зарезервированные слова: 9
signed – знаковый; unsigned – беззнаковый; long – длинный; short – короткий. Модификаторы signed и unsigned могут применяться к типам char и int. Модификаторы short и long могут применяться к типу int. Модификатор long может применяться также к типу double. Модификаторы signed и unsigned могут комбинироваться с модификаторами short и long применительно к типу int. В табл. 2.1 приведены все возможные типы данных с различными модификациями модификаторов. Таблица 2.1 Типы данных и модификаторы Тип
char unsigned char signed char int unsigned int signed int short int unsigned short int signed short int long int signed long int unsigned long int float double long double
Размер в байтах
1 1 1 2 2 2 2 2 2 4 4 4 4 8 10
Интервал изменений
От От От От От От От От От От От От От От От
–128 до 127 0 до 255 –128 до 127 –32768 до 32767 0 до 65535 –32768 до 32767 –32768 до 32767 0 до 65535 –32768 до 32768 –2147483648 до 2147483647 –2147483648 до 2147483647 0 до 4294967295 3.4E–38 до 3.4E+38 1.7E–308 до 1.7E+308 3.4E–4932 до 3.4E+4932
Запись 3.4E-38 соответствует числу 3.4*10–38, это так называемый научный формат записи числа с плавающей точкой. Различие между целыми числами со знаком и целыми числами без знака состоит в том, как интерпретируется старший бит целого числа. Если старший бит целого числа со знаком равен нулю, – число положи10
тельное, если же старший бит равен единице, – число отрицательное. Например, целое число +5 типа int будет храниться в памяти компьютера в виде 0000000000000101 Если объявлено целое отрицательное число, то компилятор генерирует обратный код. Чтобы получить число –5 надо поменять значения всех битов на обратные, т.е. 0 заменить на 1, 1 заменить на 0 и прибавить к младшему биту 1. Число –5 в двоичной записи в обратном коде будет иметь вид 1111111111111011 2.2. Основная форма объявления переменных Здесь тип должен быть одним из существующих в C типов переменных, а список переменных может состоять из одной или нескольких переменных, разделенных запятыми. При объявлении переменных компилятор выделяет место в памяти компьютера, необходимое для размещения переменной указанного типа. Примеры объявления переменных int a,b,c; float radius,length; unsigned char ch1,ch2; long double integral; Очень важное значение имеет вопрос о месте объявления переменной в программе. Правило, определяющее место объявления переменной в программе, называется правилом видимости. В языке C могут быть три места, где переменная может быть объявлена. Во-первых, вне каких либо функций, в том числе и main(). Такая переменная называется глобальной и может использоваться в любом месте программы ( за исключением глобальных статических переменных, речь о которых далее). Во-вторых, переменная может быть объявлена внутри блока, в том числе внутри тела функции. Такая переменная называется локальной и может использоваться только внутри этого блока. Такая переменная неизвестна вне этого блока. Кроме того, переменная может быть объявлена как формальный параметр функции. Переменная, объявленная как формальный 11
параметр функции, используется для передачи информации этой функции, а также может рассматриваться как локальная переменная функции. Рассмотрим пример объявления переменных в разных местах программы #include /* Пример 3 */ #include <stdio.h> /* объявляем переменные в разных местах программы */ char ch; /* объявление глобальной переменной*/ main() { int n; /* объявление локальной переменной функции main() */ printf(“ Введите символ “); ch=getche(); /* использование глобальной переменной */ printf(“ Введите количество символов в строке“); scanf(%d,&n) /* использование локальной переменной*/ print_str(n); /* обращение к функции посимвольной печати строки*/ } print_str(int m) /* заголовок функции print_str() с объявлением формального параметра m*/ { int j; /* объявление локальной переменной для функции print_str*/ for (j=0; j<m; j++) /* использование локальной переменной j*/ printf(“%c\n”,ch); /* использование глобальной переменной ch*/ } При написании программ необходимо помнить следующие правила: – две глобальные переменные не могут иметь одинаковые имена; – локальная переменная одной функции может иметь такое же имя, как локальная переменная другой функции (или формальный параметр другой функции); – две локальные переменные в одном блоке не могут иметь одинаковые имена, в том числе формальный параметр функции не должен совпадать с локальным параметром, объявленным в функции.
12
2.3. Константы В языке C константы представляют фиксированные величины, которые не могут быть изменены в программе. Константы могут быть любого базового типа данных. Примеры констант char int unsigned int long int short int float double
‘a’, ‘\n’, ‘8’ 1, 134, -580 52500 87000, -37, 7L 11, 13, -128 133.34, 3.36E-6, 5E+5 133.34, 133340, -2.789
Правила определения типа констант следующие. Целая константа относится к типу int, если эта константа входит в интервал значений типа int. Если эта константа не ходит в интервал значений типа int, например 37750, то она считается константой типа unsigned. Если же константа не входит в интервал изменения unsigned, она считается константой типа long. Константа с десятичной точкой считается константой типа double, если она помещается в соответствующий интервал измерения. Для явного задания типа констант используется механизм суффиксов. В качестве суффиксов целочисленных констант используются u,l,h,U,L,H. Для чисел с плавающей точкой – l,L,f,F. Например 13h, 35H 25L, -223l 87lu 88Lu 89ul 55uh unsigned 27.43f 7.7E-6F 1.41l 3.2E+12L
short int long int unsigned long short float double
В программировании важную роль играют восьмеричные и шестнадцатеричные константы. Перед шестнадцатеричной константой ставится пара 0x. Восьмеричная константа всегда начинается с нуля. Шестнадцатеричные и восьмеричные константы всегда являются беззнаковыми. В качестве цифр восьмеричных констант используются символы – 0,1,2,3,4,5,6,7. В качестве цифр шестнадцатеричных кон13
стант используются символы – 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F. В табл. 2.2 представлено соответствие между десятичными и шестнадцатеричными числами, а также записями восьмеричных и шестнадцатеричных констант. Таблица 2.2 Числа и константы Десятичное число
Шестнадцатеричное число
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
0 1 2 3 4 5 6 7 8 9 A B C D E F
Двоичная Восьмеричная Шестнадцатеричная запись числа константа константа
0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
0×0 0×1 0×2 0×3 0×4 0×5 0×6 0×7 0×8 0×9 0×A 0×B 0×C 0×D 0×E 0×F
00 01 02 03 04 05 06 07 10 11 12 13 14 15 16 17
Для представления в двоичном виде шестнадцатеричного числа надо просто заменить двоичной записью каждую цифру этого числа. Например, число 0xAC1F представимо в виде A 1010
C 1100
1 0001
F 1111
Строковые константы (strings) также играют в языке C важную роль. Строковая константа (строка) представляет собой набор символов, заключенный в двойные кавычки. Особенностью представления таких констант в памяти компьютера является то, что необходи14
мо отводить на один байт больше, чем требуется для размещения всех символов строки. Этот последний байт заполняется нулевым значением, называется нулевым байтом и имеет специальное обозначение ‘\0’. Нельзя путать строковые константы с символьными константами. Так “b” – это строковая константа, содержащая одну букву, а ‘b’ – символьная константа, или просто символ. Отличие “b” от ‘b’ в том, что строка “b” содержит еще один символ ‘\0’ в конце строки, “b” занимает в памяти 2 байта, в то время как ‘b’ – только один байт. В языке C есть символьные константы, которые не соответствуют никакому из печатных символов. Так, в коде ASCII символы с номерами от нуля до 31 являются управляющими символами, которые нельзя ввести с клавиатуры. Для использования таких символов вводятся так называемые управляющие константы. Управляющие символы представлены в табл. 2.3: Таблица 2.3 Управляющие символы Управляющий символ
\b \f \n \r \t \v \" \' \\ \0 \a \N \xN \?
Значение
BS, забой Новая страница, перевод страницы Новая строка, перевод строки Возврат каретки Горизонтальная табуляция Вертикальная табуляция Двойная кавычка Апостроф Обратная косая черта Нулевой символ, нулевой байт Сигнал Восьмеричная константа Шестнадцатеричная константа Знак вопроса
Если за символом обратной черты следует символ не из этой таблицы, то эта пара воспринимается просто как соответствующий символ. 15
2.4. Символьные переменные и строки Символьная переменная – это величина размером в 1 байт, которая используется для представления литер и целых чисел в диапазоне от 0 до 255 или от –128 до 127, в зависимости от того, знаковая переменная или беззнаковая. Символьные константы заключаются в одинарные кавычки. Примеры символьных констант: ‘d’, ‘+’, ‘8’. Приведем пример программы с использованием символьных переменных и констант # include < stdio.h > /* Пример 4 */ main() { char ch; ch=’c’; printf(“%c”,ch); ch=’+’; printf(“%c%c”,ch,ch); } В функции prinf() появилась новая спецификация – %c. В таком формате печатается символ. Этот же формат можно использовать в функции scanf() для ввода символа с клавиатуры. В языке С в стандартной библиотеке ввода/вывода есть специальная функция getche(). Эта функция ожидает, пока не будет нажата какая-либо клавиша клавиатуры, и затем вводит код этой клавиши. Рассмотрим пример программы, использующей указанную функцию # include < conio.h > # include < stdio.h > /* Пример 5 */ main() { char ch; printf(“Нажмите любую клавишу”); ch=getche(); if ( ch == ‘a’) printf(“Вы нажали клавишу a\n”); printf(“ Вы нажали клавишу %c”,ch); } 16
В языке C строка – это массив символов, заканчивающихся нулевым байтом. В языке C нет стандартного типа строка (в отличие от языка Pascal) и строка объявляется как одномерный массив символов, но для работы с массивом символов как со строкой имеется набор библиотечных функций. Одномерный массив – это упорядоченная последовательность данных одного типа. В программе одномерный массив символов объявляется как char str[80]; В этом описании char – тип элементов массива, str – имя массива, в квадратных скобках указан размер массива – 80. Для обращения к отдельному элементу массива нужно указать после имени массива номер элемента в квадратных скобках, например – str[12]. В языке С все элементы массива нумеруются начиная с нуля, т. е. str[0] – первый элемент массива, str[2] – второй элемент массива, str[79] – 80-й элемент массива. Следует помнить, что в С строка – это массив символов, заканчивающихся нулевым байтом, поэтому при объявлении массива, с которым необходимо работать как со строкой, следует зарезервировать место под нулевой байт. Например, если слово english – это символьная строка, то под нее нужно зарезервировать массив из восьми символов: семь для букв, один последний символ для нулевого байта. Для чтения строки с клавиатуры необходимо создать символьный массив и затем использовать библиотечную функцию gets(). В качестве аргумента функции gets() используется имя массива, куда вводится строка. Функция gets() читает символы с клавиатуры до тех пор, пока не будет нажата клавиша Enter. Нажатие этой клавиши устанавливает в конец строки нулевой байт. Пример программы, где используется ввод строки с клавиатуры # include < stdio.h > /* Пример 6 */ main() { char str[80]; printf(“ Введите ваше имя “); gets(str); printf(“ Ваше имя - %s”,str); } 17
В данном примере используется спецификатор %s. Он предназначен для ввода/вывода строк. 2.5. Инициализация переменных После того как переменная объявлена, ей в процессе выполнения программы должно быть присвоено значение. Язык С предоставляет возможность программисту присвоить значение переменной одновременно с процессом ее объявления. Основная форма инициализации переменной имеет вид тип имя переменной = константное выражение; Например: int a=230; char c=’c’, ch=’0’; Объявление переменной с одновременной инициализацией ее значения приводит к тому, что одновременно с выделением памяти в эту память записывается значение инициализации. Глобальные или статические переменные всегда инициализируются либо нулем, либо значением инициализатора. Локальные переменные остаются неопределенными до первого присвоения им значения. Глобальные переменные инициализируются только один раз в начале выполнения программы. Локальные переменные инициализируются при каждом вызове функции. В стандарте ANSI для С инициализировать можно только константным выражением. В реализации языка Borland C инициализировать можно не только константой, но и выражением с использованием значений переменных, которые были ранее определены (динамическая инициализация). 2.6. Операции В состав языка С входит большое число разнообразных операций. Знак операции в языке С – это некоторый символ или комбинация символов, которые сообщают компилятору о необходимости выполнить определенные арифметические, логические или другие действия. Каждая операция, кроме своего содержательного смысла, также определяется рядом характеристик: типы и число операндов, порядок своего выполнения, приоритет выполнения по отношению к другим операциям. Рассмотрим различные виды операций. Арифметические операции. К арифметическим операциям относятся: 18
- вычитание и унарный минус; + сложение; * умножение; / деление; % деление по модулю; ++ увеличение на единицу (инкрементация ); - - уменьшение на единицу (декрементация ). Операции сложения, вычитания, умножения и деления действуют так же, как и в большинстве других языках программирования. Они могут применяться ко всем встроенным типам данных. Операции выполняются слева направо, т. е. сначала вычисляется выражение левого операнда, затем выражение, стоящее справа от знака операции. Если операнды имеют один тип, то результат арифметической операции имеет тот же тип. Поэтому, когда операция деления / применяется к целым переменным или символьным переменным, остаток отбрасывается. Операция деление по модулю % дает остаток от целочисленного деления. Такая операция может применяться только к целочисленным переменным. Язык С предоставляет программисту еще две очень полезные и специфические операции – унарные операции ++ и —. Операция ++ прибавляет единицу к операнду, операция – вычитает единицу из операнда. Обе операции могут следовать перед операндом или после операнда (префиксная и постфиксная формы). Три написанные ниже оператора дают один и тот же результат, но имеют различие при использовании в выражениях a=a+1; ++a; a++. Рассмотрим программу, позволяющую понять это различие # include < stdio.h > /* Пример 7 */ main() { int a=10; int b=70; a++; ++b; printf(“a=%d b=%d\n”,a,b); printf(“a=%d b=%d\n”,a++,++b); } 19
Результатом выполнения этой программы будет следующее: a=11, b=71; a=11, b=72. Значение переменной a не изменилось при втором обращении к функции printf(), а значение переменной b увеличилось на единицу. На самом деле значение переменной a также увеличилось на единицу, но уже после выхода из функции printf(). Различие в использовании префиксной и постфиксной форм состоит в следующем: a++ – значение переменной a сначала используется в выражении, и лишь затем увеличивается на единицу; ++a – переменная a сначала увеличивается на единицу, а затем ее значение используется в выражении. Старшинство арифметических операций следующее: ++, — - (унарный минус) *, /, % +, Операции, одинаковые по старшинству, выполняются в порядке слева направо. Порядок следования операций можно изменить, используя в выражениях круглые скобки. Операции отношения и логические операции. Операции отношения используются для сравнения. Полный список операций отношения в языке С следующий: < меньше, больше, >= больше или равно, == равно, != не равно. Также имеется три логические операции: && и (AND) || или (OR) ! не (NOT). Операции отношения используются в условных выражениях. Примеры условных выражений x=199, ‘c’==’C’ 20
Каждое условное выражение проверяется: истинно оно или ложно, т. е. каждое условное выражение, при своем выполнении, принимает значение “истинно” (“true”) или “ ложно” (“false”). В языке С нет логического типа данных. Поэтому результатом логического выражения является целочисленное значение. В языке С “ истинно” – это ненулевая величина, “ ложно” – это нуль. В большинстве случаев в качестве ненулевой величины используется единица. Рассмотрим пример #include < stdio.h > /* Пример 8 */ main() { int tr, fal; tr=(99101); /* выражение ложно */ printf(“true - %d false - %d\n”,tr,fal); } Логические операции в языке С соответствуют классическим логическим операциям AND(&&), OR(||) и NOT(!), а их результат приводится в табл. 2.4 . Таблица 2.4 Логические операции и их результат X
Y
X AND Y
X OR Y
NOT X
X XOR Y
0 1 X 0 1
0 0 Y 1 1
0 0 X AND Y 0 1
0 1 X OR Y 1 1
1 0 NOT X 1 0
0 1 X XOR Y 1 0
Операция XOR называется операцией “исключающее или”. В языке С нет знака логической операции XOR, хотя она может быть реализована с помощью операций AND, OR, NOT. Далее будут рассмотрены побитовые операции, среди которых операция “исключающее или” уже есть. Операции отношения и логические операции имеют более низкий приоритет, чем арифметические операции. Старшинство логических операций и операций отношения следующие: 21
! > == && ||
< !=
>=
применимы только к целочисленным переменным. В результате применения этих операций сдвигаются все биты левого операнда на число позиций, определяемого выражением справа от знака операции влево или вправо. Форма этих операций следующая: value > число позиций. Пример: двоичное представление числа x=9: 00001001, тогда x=93 00000001 x=9>>5 00000000 Применение операций сдвига может приводить к потере младших или старших байтов. Применение операций > по очереди к одной и той же переменной может изменить значение этой переменной из-за потери разрядов. Поразрядные операции порождают еще несколько сложных операций присваивания: |=, &=, ^=, =. Операции ( ) и [ ]. В языке С круглые и квадратные скобки также рассматриваются как операции. Причем эти операции имеют высший приоритет. Операция условие? Операция условие – единственная операция языка С, имеющая три операнда. Эта операция имеет вид ( выр1 )?(выр2):(выр3) Вычисляется выражение (выр1). Если это выражение имеет ненулевое значение, то вычисляется выражение (выр2), и оно будет результатом выполнением операции. Если значение выражения (выр1) равно нулю, то вычисляется значение выражения (выр3) и его значение будет результатом операции. В любом случае вычисляется только одно из выражений: (выр2) и (выр3). Например, операцию условие удобно применять для нахождения наибольшего из двух чисел a и b max=(a>b)?a:b; или для нахождения абсолютной величины числа a abs=(a>0)?a:-a; Если второй и третий операнды являются величинами типа lvalue, т. е. могут стоять в левой части операции присваивания, то и результат операции условие является величиной типа lvalue. С помощью этой 24
операции можно просто решить такую задачу: найти наибольшее из двух чисел заменить его на единицу: (a>b)?a:b=1; Операция запятая. Операция запятая имеет самый низкий приоритет из всех операций языка С. Она выполняется слева направо, и ее значением является значение правого операнда. В выражении (выр1), (выр2) сначала вычисляется значение (выр1), а затем – значение (выр2). Это значение и будет результатом операции. Операция sizeof.. Операция sizeof имеет две формы: sizeof(тип) или sizeof(выражение). Результатом этой операции является целочисленная величина типа или выражения в байтах. При использовании второй формы значение самого выражения не вычисляется, а лишь определяется его тип. Примеры использования: sizeof( double ) или sizeof( a ). В качестве типа в операции sizeof не может использоваться тип void. В дальнейшем покажем, что ряд операций в языке С имеют двойное назначение, например операция &. Кроме того операции . и -> рассмотрим позднее. 2.7. Выражения Выражения в языке С – это некоторая допустимая комбинация переменных, констант и операций. Каждое выражение принимает какое-либо значение. В выражениях языка С допустимо смешение переменных разного типа. Правила языка С, которые используются для автоматического приведения типов при вычислении арифметического выражения, следующие: 1. Все переменные типа char и short int преобразуются в int, все переменные типа float преобразуются в double. 2. Для любой пары операндов, если один из операндов long double, то и другой преобразуется в long double; если один из операндов double, то и другой преобразуется в double; если один из операндов long, то и другой преобразуется в long; если один из операндов unsigned, то и другой преобразуется в unsigned. 3. В операторе присваивания конечный результат приводится к типу переменной в левой части оператора присваивания, при этом тип может как повышаться так и понижаться. Тип результата вычисления выражения можно изменить, используя конструкцию “приведение”, имеющую следующий вид: ( тип ) выражение 25
Здесь “тип” – один из стандартных типов данных. Например, если необходимо сделать результат деления переменной a типа int на 2 типом float, надо записать ( float ) a/2. Значение выражения (float) (a/2) будет другим, чем в первом случае. Это связано с тем, что в первом случае приведение типа применялось к переменной а, а во втором случае – к результату вычисления выражения в круглых скобках а/2. Пробелы и круглые скобки в выражениях расставляются так как необходимо программисту. При компиляции лишние пробелы просто игнорируются.
26
3. ОПЕРАТОРЫ 3.1. Условный оператор Основная форма условного оператора выглядит следующим образом: if ( условие ) оператор; else оператор; Если значение условия “ истинно”, то выполняется оператор (им может быть составной оператор), следующий за условием. Если же условие принимает значение “ ложно” то выполняется оператор, следующий за словом else. В записи оператора if вторая часть (т. е. оператор else) может отсутствовать. Тогда, если условие принимает значение “ ложно”, выполняется следующий оператор программы. В качестве условия может стоять произвольное выражение. В операторе if лишь проверяется, является ли значение этого выражения ненулевым (истинным) или нулевым (ложным). Рассмотрим пример программы определения знака вводимого с клавиатуры вещественного числа с использованием оператора if # include < stdio.h > /* Пример 9 */ main() { int sgn; float x; printf (“ Введите число “); scanf (“%f”,&x); if ( x>0 ) { sgn=1; printf(“Число %f положительное sgn = %d\n”,x,sgn); } if ( x==0 ) { sgn=0; printf(“Число %f равно нулю sgn = %d\n”,x,sgn);} if ( x /* Пример 10 */ main() { int sgn; float x; printf(“ Введите число “); scanf(“%f”,&x); if ( x>0 ) { sgn=1; printf(“Число %f положительное sgn = %d\n”,x,sgn); } else if (x /* Пример 12 */ main() { char ch; printf(“ Введите заглавную букву русского алфавита ”); ch=getchar(); if ( ch >= ‘А’ && ch /* Пример 13 */ main() { char ch; printf(“ Введите заглавную букву русского алфавита ”); ch=getchar(); if ( ch >= ‘А’ && ch /* Пример 14 */ main() { int i,j; for ( i=1; i # include < time.h > /* Пример 15 */ /* Угадываем число, заданное в диапазоне от 1 до 100 */ main() { int s,x; int n=0; randomize(); s=random(100)+1; /* генерация случайного числа */ do { printf(“ Введите число от 1 до 100:“); scanf(“%d”, &x); n++; if ( s<x ) printf(“ Заданное число меньше\n”); if ( s>x ) printf(“ Заданное число больше\n”); } while ( s-x ); printf(“ Вы угадали число \n”); printf(“ Затратили на угадывание %d попыток\n”,n); } В отличие от оператора for, в теле цикла операторов while и do-while (если оно не пустой оператор) обязательны действия, направленные на 34
изменения значения условия. Если таких действий нет, то циклы while и do-while могут стать бесконечными. В вышеуказанном примере это действия, связанные с изменением значения переменной x. 3.4. Операторы break и continue Оператор break имеет два применения. Первое – окончание case в операторе switch. Второе – немедленное окончание цикла, не связанное с проверкой обычного условия окончания цикла. Когда оператор break встречается внутри оператора цикла (является частью тела цикла), то происходит немедленный выход из цикла и переход к выполнению оператора, следующего за оператором цикла: # include < stdio.h > /* Пример 16 */ main() { int i; for ( i=0; i 10000 ) break } } В данном примере выход из цикла произойдет, когда значение куба переменной i превысит величину 10000. Еще один полезный оператор – continue. Если оператор continue встретился в теле цикла, то он передает управление на начало следующей итерации цикла. В циклах while и do-while на проверку условия, в цикле for – на изменение параметра цикла. Этот оператор необходим, когда надо закончить текущую итерацию цикла и не выполнять оставшиеся операторы, входящие в тело цикла # include < stdio.h > /* Пример 17 */ main() { int i; for ( i=7; i /* Пример 18 */ /* Ввод строки с клавиатуры и вывод ее на экран */ main() { char str[80]; printf(“Введите строку длиной не более 80 символов”); gets(str); printf(“ Вы ввели строку %s\n”,str); printf(“Введите еще одну строку ”); scanf(“%s”,str); printf(“ Вы ввели строку ”); puts(str); } Для работы со строками существует специальная библиотека, описание которой находится в файле string.h. Рассмотрим их подробнее. Вызов функции strcpy() имеет вид – strcpy(s1,s2). Эта функция используется для копирования содержимого строки s2 в строку s1. Массив s1 должен быть достаточно большим, чтобы в него поместилась s2. Если места мало, то компилятор не выдаст соответствующего сообщения об ошибке, также это не приведет к прерыванию выполняемой программы, но может привести к порче других данных, что отразится на результате. Вызов функции strcat() имеет вид – strcat(s1,s2). Эта функция присоединяет строку s2 к строке s1 и помещает ее в массив, где находится строка s1, при этом строка s2 не изменяется. Нулевой байт, который завершал строку s1, будет заменен первым символом строки s2. И в функции strcpy(), и в функции strcat() полученная строка автоматически завершается нулевым байтом. Рассмотрим пример использования этих функций: 39
# include < stdio.h > # include < string.h > /* Пример 19 */ main() { char s1[20], s2[20]; strcpy(s1,” Hello, “); strcpy(s2,” World !”); puts(s1); puts(s2); strcat(s1,s2); puts(s1); puts(s2); } Вызов функции strcmp() имеет вид – strcmp(s1,s2). Эта функция сравнивает строки s1 и s2 и возвращает значение ноль, если строки равны, т. е. содержат одно и то же число одинаковых символов. Под сравнением строк понимается сравнение в лексикографическом смысле. Если s1 лексикографически больше s2, то функция strcmp() возвращает положительное значение, если меньше – отрицательное. Вызов функции strlen() возвращает длину строки s, при этом завершающий нулевой байт не учитывается. Вызов strlen(“hello”) вернет значение 5. Рассмотрим пример программы, в которой определяется длина строки, вводимой с клавиатуры: # include < stdio.h > # include < string.h > /* Пример 20 */ main() { char s[80]; printf(“ Введите строку: ”); gets(s); printf(“строка \n%s\n имеет длину %d символов\n”,s,strlen(s)); }
40
4.3. Двумерные массивы Язык С допускает многомерные массивы, простейшими из которых являются двумерные массивы. Можно сказать, что двумерный массив – это массив одномерных массивов. Двумерный массив int a[3][4] можно представить в виде табл. 4.1. Таблица 4.1 Двумерный массив int a[3][4]
a[0][0] a[1][0] a[2][0]
a[0][1] a[1][1] a[2][1]
a[0][2] a[1][2] a[2][2]
a[0][3] a[1][3] a[2][3]
Здесь первый индекс – номер строки, второй номер столбца. Количество байт памяти, необходимое для хранения двумерного массива в памяти определяется как **. В памяти компьютера двумерный массив располагается по строкам. Память для массивов, которые определены как глобальные, отводится в процессе компиляции и сохраняется на все время выполнения программы. Часто двумерные массивы используются для работы с числовыми и с символьными таблицами(массивы строк). Рассмотрим пример: # include < stdio.h > # include < string.h > /* Пример 21 */ main() { char text[5][20]; strcpy(text[0],”Turbo Basic”); strcpy(text[1],”Turbo Pascal”); strcpy(text[2],”Borland C++”); strcpy(text[3],”Turbo Prolog”); strcpy(text[4],”Turbo Fortran”); } В данной прграмме заполняется массив text[][], причем, в функции strcpy при заполнении массива, используется только первый индекс. Заполнение text[][] иллюстрируется табл. 4.2. 41
Таблица 4.2 Расположение массива text[][] в памяти компьютера
T T B T T
u u o u u
r r r r r
b b l b b
o o a o o
B a s i c \0 P a s c a l \0 n d C + + \0 P r o l o g \0 F o r t r a n \0
4.4. Инициализация массивов При программировании бывает важным уметь инициализировать массивы, т. е. присваивать элементам массива некоторые начальные значения. Самый простой способ инициализации – указать список инициализаторов в фигурных скобках при объявлении массива, например: float ff[5]={1.4, 2.5, 3.6, 12.8, 0.9}; int z[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; Многомерные массивы можно инициализировать, рассматривая их как массивы массивов: char str z[3][4]={ {1,2,3,4},{5,6,7,8},{9,10,11,12}}; Количество инициализаторов может быть меньше, чем количество элементов массива. В этом случае оставшиеся элементы массива считаются неопределенными. Символьные массивы могут инициализироваться как обычный массив: char str[12]={‘a’,’b’,’c’,’d’}; а могут – как строка символов: char str[12]=“abcd”; Отличие состоит в том, что во втором случае будет добавлен автоматически нулевой байт. Допускается объявление и инициализация массива без явного указания его размера: char str[]=”abcd”; В этом случае компилятор сам определит необходимое количество элементов массива, включая нулевой байт. Таким образом можно объявлять массивы любого типа: int a[]={10, 20, 30, 40, 50}; 42
Но при объявлении многомерных массивов с неизвестным количеством элементов, можно не указывать размер только в самых левых квадратных скобках: int a[][3]={1, 2, 3 5, 6, 7 8, 9, 10}; Рассмотрим пример использования массивов на примере задачи сортировки одномерного целочисленного массива по убыванию значений: # include < stdio.h > /* Пример 22 */ main() { int arr[10]={9, 12, 43, 2, 4, 78, 15, 34, 11, 27}; int i,j,tmp; printf(“ Неотсортированный массив: ”); for ( i=0; i /* Пример 26 */ main() { char str[]=”String From Letters in Different Registers”; int i; printf(“Строка Будет Напечатана Заглавными Буквами”); while ( str[i] ) printf(“%c”, toupper(str[i++])); } # include < stdio.h > # include < ctype.h > main() { char str[]=”String From Letters in Different Registers”; 47
char *p; p=str; printf(“Строка будет напечатана строчными буквами”); while ( str[i] ) printf(“%c”, tolower(*p++)); } Если в этих примерах заменить строку на английском языке на строку, набранную русскими буквами, то никакого преобразования букв в строчные, или наоборот, в прописные не произойдет. Это связано с тем, что стандартные функции toupper() и tolower() анализируют значения аргумента и возвращают то же самое значение, если он не является соответственно строчной или прописной буквой латинского алфавита. Если же аргумент является строчной буквой латинского алфавита, то значением функции toupper() будет соответствующая прописная буква (точнее, код этой буквы). Функция tolower() изменяет код только прописных букв латинского алфавита. Прототипы этих функций находятся в файле ctype.h. 4.7. Массивы указателей Указатели, как и переменные любого другого типа, могут объединяться в массивы. Объявление массива указателей на 12 чисел имеет вид int *x[11]; . Каждому из элементов массива можно присвоить адрес; например, пятому элементу этого массива присвоим адрес целой и ранее объявленной переменной y: x[4]=&y;. Если затем необходимо найти значение переменной y, то это можно сделать, выполнив операцию *x[4]. Рассмотрим пример использования массива указателей: # include < stdio.h > # include < string.h > # include < stlib.h > # include < conio.h > /* Пример 27 */ main() { char *ext[]={“exe”,”com”,”dat”,”c”,”pas”,”cpp”}; char ch, sl[80]; for( ; ; ) { do 48
{ printf(“ Файлы с расширением:\n”); printf(“1. exe\n”); printf(“2. com\n”); printf(“3. dat\n”); printf(“4. c\n”); printf(“5. pas\n”); printf(“6. cpp\n”); printf(“7. Exit\n”); printf(“ Ваш выбор: \n”); ch=getche(); printf(“\n”); } while ((ch’7’)); if ( ch == 7 ) break; strcpy(sl,”dir *.”); strcat(sl,ext[ch-49]; system(sl); } } Данная программа формирует командную строку с учетом пожеланий пользователя и затем выполняет ее, используя библиотечную функцию system(). Данная функция выполняет указанную в командной строке команду (dir – вывод справочника файлов). Расширение для имен выводимых файлов задается пользователем. Константа 49 условно состоит из двух слагаемых – 48 и 1. 48- это символьный код нуля. Поэтому, если пользователь программы хочет вывести файлы с расширением pas , он указывает символьную пятерку, которой соответствует код – 53. Разность 53-49 определяет индекс пятого элемента в массиве ext (первый элемент массива в С имеет нулевой индекс). Часто массив указателей используется, если надо иметь ссылки на стандартный набор строк. Например, для хранения сообщений о возможных ошибках удобно сделать так: char *errors[]={“Cannot open file”, “Cannot close file”, “Allocation error”, “System error” }; 49
При таком объявлении строчные константы будут занесены в раздел констант в памяти, массив указателей будет состоять из четырех элементов, под которые будет выделена память, и эти элементы будут инициированы адресами, указывающими на начало строчных констант. Строчная константа в языке С ассоциируется с адресом начала строки в памяти, тип строки получает char*(указатель на тип char). Поэтому возможно и активно используется следующее присваивание: char *p; p=”Hello, World !”; В языке С возможна ситуация, когда указатель указывает на указатель. В этом случае описание имеет следующий вид: int **point; Здесь point имеет тип указатель на указатель int. Соответственно, чтобы получить целочисленное значение переменной, на которую указывает point, надо в выражении использовать **point. Рассмотрим пример: # include < stdio.h > /* Пример 28 */ main() { int i; int *pi; int **ppi; i=12; pi=&i; ppi=π printf(“i = %d pi = %p ppi = %p **ppi = %d\n”,i,pi,ppi,**ppi); } После того, как указатель был объявлен, но до того, как ему было присвоено значение, указатель содержит неопределенное значение. Попытка использовать такое значение может вызвать ошибку при выполнении программы, и даже, нарушить работу операционной системы. Принято считать, что указатель с неопределенным значением должен иметь значение null, но это не делает такой указатель “безопасным”. С другой стороны нулевой указатель иногда удобно использовать как признак окончания некоторого массива указателей или списка, поэтому он часто фигурирует в операциях сравнения. 50
5. ФУНКЦИИ 5.1. Объявление функций. Оператор return Функция – это некоторая логически законченная совокупность операторов языка, которая выполняет определенную конкретную задачу, и может быть вызвана для своего выполнения необходимое количество раз. Основная форма описания функции имеет вид: Тип < имя функции > ( список параметров ) { тело функции } Тип определяет тип значения, которое возвращает функция с помощью оператора return. Если тип не указан, то по умолчанию предполагается, что функция возвращает целое значение ( типа int ). Список параметров состоит из перечня типов и имен параметров, разделенных запятыми. Функция может не иметь параметров, но круглые скобки в ее описании необходимы в любом случае. В списке параметров для каждого параметра должен быть указан тип. Оператор return имеет два варианта использования. Во-первых, этот оператор вызывает немедленный выход из текущей функции и возврат в вызывающую программу. Вовторых, этот оператор может использоваться для возврата значения функции. В теле функции может быть несколько операторов return, но может быть ни одного. В этом случае возврат в вызывающую программу происходит после выполнения последнего оператора в теле функции. Приведем пример функции, реализующей возведение числа a в натуральную степень b : /* Пример 29 */ float step(float a, int b) { float i; 51
if ( a < 0 ) return (-1); for ( i=1; i b ) m=a; else m=b; return m; } Можно написать эту функцию, не используя дополнительную переменную: /* Пример 31 */ int max( int a, int b) { if ( a > b ) return a; else return b; } Еще короче: int max( int a, int b) { if ( a > b ) return a; return b; } В случае, когда оператор return отсутствует в теле функции, или за ним нет значения, то возвращаемое значение не определено. Если функция должна возвращать значение в соответствии со своим описанием, но не делает этого, компилятор выдаст предупреждение. Все функции, возвращающие значения, могут использоваться в выражениях языка С, но они не могут использоваться в левой части оператора присваива52
ния, за исключением тех случаев, когда возвращаемым значением является указатель. Использование функций, возвращающих указатели, имеет некоторые особенности, так как их значениями являются адреса памяти данных определенного типа. Рассмотрим пример функции, возвращающей указатель на тип char. Эта функция находит в строке первый пробел и возвращает его адрес: /* Пример 32 */ char* find( char* string ) { int i=0; while (( string[i] != ‘ ‘) && (string[i] != ‘\0’)) i++; if ( string[i] == ‘ ‘ ) return &string[i]; else return NULL } Когда функция не должна возвращать никакого значения, она имеет тип void. Например, для вывода на экран горизонтальной строчки, состоящей из заданного символа, начиная с текущего положения курсора, можно использовать следующую функцию: /* Пример 33 */ void gorisont_line( char ch ) { int i; for ( i=0; i ( список параметров ); Использование прототипа функцией является ее объявлением. Чаще всего прототип функции полностью совпадает с заголовком в описании функции, хотя это и не всегда так. При объявлении функции компилятору важно знать имя функции, количество и тип параметров и тип возвращаемого значения. При этом имена формальных параметров не играют никакого значения и игнорируются компилятором. Поэтому прототип функции может выглядить как: /* Пример 34 */ int func( int a, float b, char* c); или так int func( int , float , char* ); Рассмотрим пример: # include < stdio.h > float sqr( float a ); main() { float b=7.8; printf(“ Квадрат числа %f равен %f”,b,sqr(b)); } float sqr(float a) { return a*a } Если функция не имеет аргументов, то при объявлении прототипа такой функции следует вместо аргументов писать ключевое слово void. Использование данного ключевого слова необходимо и при объявлении функции main(). Такое объявление может иметь вид void main(void) или main(void). 5.3. Область действия и область видимости Область действия переменной – это правила, которые устанавливают, какие данные доступны из данного места программы. В языке С каждая функция – это отдельный блок программы. Попасть в тело функции нельзя иначе, как через вызов данной функции. 54
В частности, нельзя оператором локального перехода goto перейти в середину другой функции. С точки зрения области действия переменных различают три типа переменных: глобальные, локальные и формальные параметры. Правила области действия определяют, где каждая из них может применяться. Локальные переменные – это переменные, объявленные внутри блока, в частности внутри функции. Язык С поддерживает простое правило: переменная может быть объявлена внутри любого блока программы. Локальная переменная доступна внутри блока, в котором она объявлена. Вспомним, что блок открывается фигурной скобкой и закрывается фигурной скобкой. Область действия локальной переменной – блок. Локальная переменная существует пока выполняется блок, в котором эта переменная объявлена. При выходе из блока эта переменная (и ее значение) теряется # include < stdio.h > void ff( void ); /* Пример 35 */ main ( void ) { int i=1; ff(); printf(“ В функции main значение i равно %c\n”,i); } void ff(void) { int i=12; printf(“ В функции ff значение i равно %c\n”,i); } Пример показывает, что при вызове функции значение переменной i, объявленной в main(), не изменилось. Формальные параметры – это переменные, объявленные при описании функции как ее аргументы. Функции могут иметь некоторое количество параметров, которые используются при вызове функций для передачи значений в тело функции. Формальные параметры могут использоваться в теле функции так же, как локальные переменные, которыми они по сути дела и являются. Область действия формальных параметров – блок, являющийся телом функции. 55
Глобальные переменные – это переменные, объявленные вне какойлибо функции. В отличие от локальных переменных глобальные переменные могут быть использованы в любом месте программы, но перед их первым использованием они должны быть объявлены. Область действия глобальной переменной – вся программа. Использование глобальных переменных имеет свои недостатки: – они занимают память в течение всего времени работы программы; – использование глобальных переменных делает функции менее общими и затрудняет их использование в других программах; – использование внешних переменных делает возможным появление ошибок из-за побочных явлений. Эти ошибки, как правило, трудно отыскать. 5.4. Классы памяти В языке С имеется инструмент, позволяющий управлять использованием памяти и , тем самым, создавать гибкие программы. Этот инструмент – классы памяти. Каждая объявленная переменная принадлежит к одному из четырех классов памяти, которые описываются следующими ключевыми словами: auto – автоматическая; extern – внешняя; static – статическая; register – регистровая. Тип памяти указывается модификатором – ключевым словом, стоящим перед указанием типа переменной. Например, static int sum; register int plus. Если ключевое слово перед спецификацией типа локальной переменной при ее объявлении нет, то по умолчанию она принадлежит классу auto. Поэтому практически это слово не используется. Автоматические переменные (auto) имеют локальную область действия. Они известны только внутри блока, в котором определены. Другие функции могут использовать то же имя, но это должны быть переменные, относящиеся к разным блокам. Автоматическая переменная создается (под нее отводится память) при входе в блок функции. При выходе из блока автоматическая переменная пропадает, а область памя56
ти, в которой находилась эта переменная, считается свободной и может использоваться для других целей. Автоматические переменные хранятся в оперативной памяти, точнее в той области памяти, которая отводится под стек. Регистровые (registr) переменные хранятся в регистрах процессора, доступ к которым значительно быстрее, чем к автоматическим переменным. В остальном регистровые переменные аналогичны автоматическим переменным. Регистровая память процессора невелика, и если доступных регистров нет, то переменная становится простой автоматической переменной. Описание регистровой переменной имеет вид: registr int q; Внешняя переменная (extern) относится к глобальным переменным. Она может быть объявлена как вне , так и внутри тела функции: void ff(void) { …………… extern int x; /* объявление переменной внутри функции*/ …………… } Появление ключевого слова extern связано с модульностью языка С, т. е. возможностью составлять многофайловую программу, где каждый файл компилируется отдельно. Когда мы в одном из файлов опишем вне тела функции глобальную переменную, например так – float global; , то для нее выделится место в памяти в разделе глобальных переменных и констант. Если мы используем эту глобальную переменную в другом файле, то при раздельной компиляции компилятор не будет знать, что это за переменная. Использование объявления extern float global; не приводит к выделению памяти, а сообщает компилятору, что такая переменная будет описана в другом файле. И тогда при компоновке программы, состоящей из нескольких файлов, компоновщик будет искать описание этой переменной и связывать ее с использованием в других файлах. Объявление внешней переменной может быть как вне функции, так и внутри функции. Если это же имя без ключевого слова extern будет объявлено внутри функции, то под этим именем будет создана уже другая автоматическая переменная. Можно к объявлению этой переменной добавить ключевое слово auto, чтобы показать, что вы не ошиблись, а намеренно продублировали имя. Объявлений переменной как внешней может быть несколько, в том числе и в одной функции 57
или в одном файле. Описание же переменной должно быть только одно. Следующие примеры демонстрируют разные способы описания внешних переменных: /* Пример 36 */ int var; /* описана внешняя переменная var */ main(void) { extern int var; /* объявлена та же внешняя переменная */ ………… } func1() { extern int var1; /* объявлена внешняя переменная var1 */ ………… /* переменная var также внешняя*/ } func2() /* переменная var внешняя */ { /* переменная var1 невидима для этой функции */ ………… } int var1; /* описание внешней переменной */ func3() /* для этой функции var1 - внешняя */ { int var; /* здесь var – локальная и не связана*/ ………… /* с соответствующей глобальной */ } func4() /* здесь var является внешней и глобальной */ { auto int var1; /* var1- локальная и автоматическая */ ………… } При описании статических переменных перед указанием типа ставится ключевое слово static. Область действия локальной статической переменной является вся программа. Место в памяти под локальные статические переменные выделяется в начале работы программы в разделе глобальных и статических переменных. Однако область видимости локальных статических переменных такая же, как и 58
у автоматических. Значение статических переменных сохраняется от одного вызова функции до другого. Локальные статические переменные инициализируются нулем, если не указан другой инициализатор. При этом описание с инициализацией static int count=12; вызывает однократную инициализацию переменной count при выделении для нее места. При последующих вызовах функции, в которой описана эта переменная, инициализации не происходит. Это позволяет использовать такую переменную для организации счетчика количества вызовов функции. # include < stdio.h > void trystat(void); /* Пример 37 */ main(void) { int i; for ( i=1; i void ff( void ); void fl( void ); /* Пример 38 */ int var=5; main( void ) { int var=10; printf(“ var = %d\n”,var); /* var = 10 */ { int var=100; printf(“ var = %d\n”,var); /* var = 100 */ } printf(“ var = %d\n”,++var); /* var = 11 */ ff(); printf(“ var = %d\n”,++var); /* var = 12*/ fl(); printf(“ var = %d\n”,++var); /* var = 13 */ fl(); 60
printf(“ var = %d\n”,++var); /* var = 14 */ } void ff( void ) { int var=55; printf(“ var = %d\n”,var); /* var = 55 */ } void fl( void ) { printf(“ var = %d\n”,var); /* var = 5 */ } 5.5. Параметры и аргументы функций В языке С все аргументы функции передаются по значению. При вызове функции в стеке выделяется место для формальных параметров функции, и в это место заносится значение фактического параметра, т. е. значение параметра при вызове функции. Далее функция использует это значение, при этом она может изменить значение параметра. При выходе из функции измененные значения параметров теряются. Таким образом, в языке С вызванная функция не может изменить значения переменных, указанных в качестве фактических параметров при обращении к ней. Например, функция swap(), которая должна менять значение параметров местами, не будет этого делать: /* Пример 39 */ void swap(int a, int b) { int tmp=a; a=b; b=tmp; } Но тем не менее функцию можно приспособить для изменения аргументов. Для этого необходимо в качестве параметра передавать адрес переменной, которую надо изменять, т. е. передавать указатель на переменную. Такой прием в языке С называется передачей параметра по ссылке. Вызванная функция должна описывать аргумент как ссылку и обращаться к фактической переменной косвенно, через эту ссылку. Если 61
в качестве аргумента берется имя массива, то передаваемое функции значение фактически есть адрес первого элемента массива. Теперь функцию swap() можно описать следующим образом: /* Пример 40 */ void swap(int *a, int *b) { int tmp=*a; *a = *b; *b=tmp; } Для иллюстрации использования этих двух способов передачи параметров приводим текст следующей программы: # include < stdio.h > /* Пример 41 */ void swap( int a, int b); void swap1( int *a, int *b); void main( void ) { int x=10, y=20; printf(“ Сначала x = %d y = %d\n”,x,y); swap(x,y); printf(“ Теперь x = %d y = %d\n”,x,y); printf(“ Ничего не изменилось \n”); swap1(&x,&y); printf(“ Теперь x = %d y = %d\n”,x,y); printf(“ Значения поменялись \n“); } void swap(int a, int b) { int tmp=a; a=b; b=tmp; } void swap1(int *a, int *b) { int tmp=*a; 62
*a = *b; *b=tmp; } Результатом работы этой программы будет следующее: сначала x=10 y=20, потом x=20 y=10. Значения переменных x и y изменились, так как были переданы по ссылке. Еще одна особенность состоит в том, что при вызове функции swap() можно задать конкретные значения, например swap(10,20). Вызвать функцию swap1() в виде swap1(&10,&20) нельзя. Если в качестве аргумента функции используется массив, есть лишь один способ – передача параметра по ссылке. Сделать это можно тремя способами: func(int ar[10]); func(int ar[]); func(int *ar); Все три способа дадут один и тот же результат. Рассмотрим программу, где используется функция сортировки массива по возрастанию значений: # include < stdio.h > /* Пример 42 */ void sort(int arr[], int n); void main( void ) { int mass[10]={1,-6,21,3,-7,4,-12,9,5,17}; int i, size=10; printf(“ до сортировки: \n“); for ( i=0; i ( стрелка, arrow). Операция стрелка употребляется вместо операции точка, когда мы хотим использовать значение поля структуры с применением переменной указателя. Вместо (*a).x можно использовать a-> x. Этот способ чаще всего и применяется. Завершая разговор о структурах необходимо сказать, что в качестве полей структуры можно использовать массивы, структуры и массивы структур. Пусть объявления переменных имеют вид Struct addr{ Char city[34]; Char street[76]; Int house; }; struct fulladdr{ struct addr addres; int room; char name[48]; } a,b; 70
Здесь addr – шаблон структуры, определенный перед объявлением структуры fulladdr и объявлением переменной а типа структуры fulladdr. Для присвоения значения полю house структуры addres переменной а используем a.adress.house = 234; 6.2. Доступ к отдельным битам В отличие от многих других языков программирования язык С обеспечивает доступ к одному или нескольким битам в байте или слове. В конкретных задачах часто бывает необходимо, чтобы некоторая переменная принимала только два значения. Для этого достаточно использовать только один бит памяти. Такая переменная, по своему содержательному смыслу, является некоторым признаком или флагом. Один из методов, встроенных в язык С и позволяющий иметь доступ к биту, – это поля битов (bit-fields). В действительности поля битов – это специальный тип членов структуры, в котором определено, из скольких бит состоит каждое поле. Основная форма объявления такой структуры следующая: struct имя_структуры { тип имя1: длина в битах; тип имя2: длина в битах; тип имя3: длина в битах; ... тип имяN: длина в битах; }; В этом объявлении структуры тип может быть одним из следующих: int, unsigned, signed. Имя1 может быть пропущено, тогда соответствующее количество бит не используется (пропускается ). Длина структуры в целом всегда кратна восьми. Так, если указать Struct onebit{ Unsigned one_bit: 1; } obj; Здесь для переменной obj будет выделено 8 бит, но использоваться будет только первый. В структуре также могут быть смешаны обычные переменные и поля битов. 71
6.3. Объединения В языке С определен еще один тип для размещения в памяти нескольких переменных разного типа – объединение( union ). Объявляется объединение также как и структура, например /* Пример 49 */ union u{ int i; char ch; long int l }; Это объявление задает шаблон объединения. Можно объявить переменные union u alfa, beta; Можно было объявить переменные одновременно с заданием шаблона. В отличие от структуры для переменной типа union места в памяти выделяется ровно столько, сколько нужно полю объединения, имеющему наибольший размер в байтах. В приведенном выше примере под переменную alfa будет выделено 4 байта памяти. Действительно, поле i требует 2 байта, поле ch – 1 байт, и поле l – 4 байта. Остальные переменные будут располагаться в том же месте памяти. Синтаксис использования полей объединения такой же, как и для структуры: u.ch = ‘N’; Для объединения также разрешена операция ->, если идет обращение к объединению с помощью указателя. Программа, приведенная ниже, выдает на экран двоичный код символа, вводимого с клавиатуры: #include < stdio.h > #include < conio > /* Пример 50 */ struct byte{ int b1: 1; int b2: 1; int b3: 1; int b4: 1; int b5: 1; 72
int b6: 1; int b7: 1; int b8: 1; }; union bits{ char ch; struct byte b; } u; void decode ( union bits b ); main ( void ) { do { b.ch:=getche(); printf(“;”); decode(u); } while ( u.ch != ‘q’); } void decode ( union bits b ) { if ( b.byte.b8 ) printf(“1”); else printf(“0”); if ( b.byte.b7 ) printf(“1”); else printf(“0”); if ( b.byte.b6 ) printf(“1”); else printf(“0”); if ( b.byte.b5 ) printf(“1”); else printf(“0”); if ( b.byte.b4 ) printf(“1”); else printf(“0”); if ( b.byte.b3 ) printf(“1”); else printf(“0”); if ( b.byte.b2 ) printf(“1”); else printf(“0”); if ( b.byte.b1 ) printf(“1”); else printf(“0”); printf(“\n”); } 73
6.4. Перечислимый тип Перечислимый тип ( enumeration ) – это множество поименованных целых констант. Перечислимый тип определяет все допустимые значения которые могут иметь переменные этого типа. Основная форма объявления такого типа следующая: enum имя_типа {список_названий} список переменных; Список переменных может быть пустым. Пример определения перечислимого типа и переменной данного типа enum season { win, spr, sum, aut }; enum season s; Ключом к пониманию сущности перечислимого типа является то, что каждое из имен win, spr, sum, aut представляют собой целую величину. Если эти величины не определены по другому, то по умолчанию они соответсвенно равны нулю, единице, двум и трем. Оператор printf(“%d %d”,win,aut); выдаст на экран числа 0 и 3. Во время объявления типа можно одному или нескольким символам присвоить другие значения, например enum value { one=1,two,three,ten=10,thousand=1000,next}; Если теперь напечатать значения printf(“%d %d %d %d %d\n”,one,two,ten,thousand,next); то на экране появятся числа 1 2 10 1000 1001, т.е. каждый следующий символ увеличивается на единицу по сравнению с предыдущим, если нет другого присваивания. С переменными перечислимого типа можно производить следующие операции: – присвоить переменную типа enum другой переменной того же типа; – провести сравнение с целью выяснения равенства или неравенства; – арифметические операции с константами типа enum (i=win+aut). Нельзя использовать арифметические операции и операции ++ и – для переменных типа enum. Основная причина использования перечислимого типа – улучшение читаемости программ. 74
6.5. Переименование типов Язык С позволяет дать новое название уже существующим типам данных. Для этого используется ключевое слово typedef. При этом создается новый тип данных. Например: typedef char SYMBOL; typedef unsigned UNSIGN; typedef float real; Достаточно часто используется оператор typedef с применением структур: /* Пример 51 */ typedef struct st_tag{ char name[40]; int kurs; char group[5]; int stip; } STUDENT; Теперь для определения переменной можно использовать struct st_tag avar; А можно использовать STUDENT avar;
75
7. ВВОД/ВЫВОД И РАБОТА С ФАЙЛАМИ 7.1. Организация ввода-вывода В языке С отсутствуют специальные операторы ввода-вывода. Все действия, связанные с вводом-выводом, выполняются с помощью функций библиотеки С. Программист может также создать свои собственные функции ввода-вывода на основе библиотечных. При вводе-выводе все данные рассматриваются как поток байтов, связанный либо с файлом на диске, либо с нефайловым физическим устройством (клавиатура, экран монитора, принтер и т. п.). Функции ввода-вывода позволяют выделять из потока и обрабатывать данные различных форматов и размеров, обеспечивая при этом буферизированный форматированный или неформатированный ввод или вывод. Для использования функций ввода-вывода необходимо директивой #include включить файл stdio.h, содержащий объявления функций ввода-вывода, а также определение констант, типов и структур, используемых этими функциями. Открытие потока осуществляется функцией fopen. При успешном открытии эта функция возвращает указатель структуры типа FILE, которая содержит информацию, необходимую для работы с потоком. При ошибке открытия возвращается значение NULL, которое определено как константа в stdio.h. Указатель потока используется в дальнейшем во всех функциях вашей программы, работающих с данным потоком. Количество одновременно открытых потоков ограничевается установками операционной системы. Параметрами функции fopen являются строка, указывающая путь к файлу и его имя, и строка, определяющая тип доступа к потоку. Литерал типа доступа может иметь значения: “r” – для чтения, “w” – для записи, “a” - для записи в конце потока, “r+” – для чтения и записи, “w+” – пустой поток для чтения и записи, “a+” – для чтения и записи в конце потока. Закрытие потока осуществляется функцией fclose. Указатель потока можно позиционировать на любое место в потоке. Для получения текущей позиции в потоке используются функции ftell и fgetpos, для изменения позиции указателя – fseek и fsetpos. В stdio.h определены стандартные указатели потоков: stdin – стандартный ввод(клавиатура), stdout – стандартный вывод(дисплей), stdprn – стандартная печать, stderr 76
– стандартный вывод сообщений об ошибках. Эти указатели можно использовать во всех функциях , требующих указатель файла, без предварительного открытия потока с помощью функции fopen. Кроме того, функции ввода-вывода на терминал (getc, putc и т.п.) не требуют указатель потока, так как используют stdin и stdout. Существуют также функции, реализующие ввод-вывод в текущее окно экрана монитора путем прямой записи в видеопамять ( getch, getche, putch, cprintf и т.д.). Эти функции требуют подключения директивой #include файла conio.h. 7.2. Классификация функций чтения и записи Классификация функций чтения и записи представлена в табл. 7.1. Таблица 7.1 Классификация функций чтения и записи Чтение Объект операции
Последовательность байт Отдельный символ
Форматированные данные
Количество элементов Введенный символ Введенное число Строка Количество введенных полей
fread fgetchar getchar
Данное типа int Строка
Возвращаемое значение
Из потока Из любого Из строки При успешном stdin потока С завершении
fgetc getc getw
gets
fgets
scanf
sscanf Запись
Объект операции
Отдельный символ Строка Форматированные данные
fputc putc putw
puts
fputs
printf
fprintf
0 EOF EOF NULL EOF
Возвращаемое значение
Из потока Из любого Из строки При успешном stdin потока С завершении
Последовательность fputchar байт putchar
При ошибке
sprintf
Выведенный символ Выведенное число Последний символ Количество выведенных байт
При ошибке
EOF EOF EOF EOF
77
Рассмотрим пример программы, которая выводит содержимое файла autoexec.bat в стандартный поток вывода, а также выводит на экран монитора последнии 15 байт этого файла. #include <stdio.h> #include /* Пример 52 */ main(void) { FILE *f; int a; long int n; if((f =fopen(“c:\\autoexec.bat”,”r”))==NULL) /*открываем поток f */ { prinf(“ Ошибка при открытии файла\n”); exit(1); } while((a=fgetc(f)) != EOF /* пока не конец файла читаем в память*/ fputc(a,stdout); /* и выводим в стандартный поток вывода*/ n=-15; fseek(f,n,SEEK_END); /* позиционирование за 15 байт до конца*/ while((a=fgetc(f)) != EOF) /* пока не конец файла читаем в память*/ putchar(a); /* и выводим на экран монитора*/ fclose(f); /* закрываем поток*/ getch(); } 7.3. Функции библиотеки ввода-вывода Библиотека функций ввода-вывода, которые можно использовать при программировании на языке С, весьма разнообразна, что определяет необходимость привести ее полностью. 7.4. Функции для работы с файлами fopen – открывает поток, связанный с файлом filename и типом доступа type. FILE *fopen(char *filename, char *type); fclose – закрывает поток stream. int fclose(FILE *stream); fcloseall – закрывает все открытые потоки. int fcloseall( void ); remove – удаляет файл с именем filename. int remove( char *filename ); rename – переименовывает файл oldname в файл newname. 78
int rename(char *oldname, char *newname); ftell – возвращает положение указателя текущей позиции файла, связанного с потоком stream. Значение возвращается в виде смещения в байтах относительно начала файла. Значение, возвращаемое функцией ftell, в дальнейшем можно использовать при вызове функции fseek. ftell возвращает положение указателя текущей позиции при успешном завершении.При ошибке возвращается значение – 1L. long int ftell( FILE *stream); fseek – устанавливает адресный указатель файла, соответствующий потоку stream, в новую позицию, которая расположена по смещению offset относительно места в файле, определенного параметром fromtwhere. Параметр fromtwhere может иметь одно из трех значений 0, 1 или 2, которые представлены тремя символическими константами, определенными в файле stdio.h, следующим образом: SEEK_SET(0) – начало файла, SEEK_CUR(1) – позиция текущего указателя файла, SEEK_END(2) – конец файла(EOF); Функция fseek возвращает значение 0, если указатель файла успешно перенесен, и ненулевое значение в случае неудачного завершения. int fseek(FILE *stream, long int offset, int fromwhere); fgetpos – сохраняет позицию указателя файла, связанного с потоком stream, в месте, указываемом параметром pos. При успешном завершении fgetpos возвращает 0. int fgetpos(FILE *stream, fpos_t *pos); Здесь и далее fpos_t - предварительно объявленный тип typedef long fpos_t. fsetpos - устанавливает указатель текущей позиции файла, связанного с потоком stream в новую позицию, которая определяется значением, получаемым предшествующим вызовом функции fgetpos. При успешном завершении fsetpos возвращает 0. Int fsetpos( FILE *stream, const fpos_t *pos); 7.5. Функции неформатированного ввода-вывода fgetc – получает символ из потока stream. int fgetc(FILE *stream); fgetchar – получает символ из потока stdin. int fgetchar( void ); 79
fgets – получает строку s длиной не более n символов из потока stream. char *fgets(char *s, int n, FILE *stream); fputc – выводит символ с в поток stream. int fput(int c, FILE *stream); fputchar- выводит символ c в поток stdout. int fputchar(int c); fputs – выводит строку символов string в поток stream. int fputs(char *string, FILE *stream); gets – получает строку символов s из потока stdin. char *gets(char *s); getc – выводит из потока stream символ этого потока. int getc(FILE *stream); getchar – выводит символ из потока stdin. int getchar( void ); putc – выводит символ c в поток stream. int putc(int c, FILE *stream); putchar – выводит символ с в поток stdout. int putchar(int c); puts – выводит строку s в поток stdout. int puts(const char *s); putw – помещает в поток stream целое значение w. int putw(int w, FILE *stream); getw – вводит из потока stream целое число. int getw(FILE *stream); 7.6. Функции блочного ввода-вывода fread – считывает n элементов данных длиной size из потока stream по адресу ptr. size_t fread(void *ptr, size_t size, size_t n, FILE *stream ); Здесь и далее size_t – предварительно объявленный в библиотеке тип typedef usigned size_t. fwrite – записывает n элементов данных длиной size из ptr и поток stream. Size_t fwrite( void *ptr, size_t size, size_t n, FILE *stream); 80
7.7. Функции форматированного ввода-вывода printf – производит форматированный вывод в stdout. int printf(const char *format [,argument,…]); scanf – выполняет форматированный вывод из потока stdin. int scanf(const char *format [,adress,…]); fprintf – посылает форматированный вывод в поток stream. int fprintf(FILE *stream, const char *format [,argument,…]); fscanf – выполняет форматированный ввод из потока stream. int fscanf(FILE *stream, const char *format [,adress,…]); sprintf – производит форматированный вывод в сстроку buffer. int sprintf(char *buffer, const char *format [,argument,…]); sscanf – выполняет форматированный ввод из строки buffer. Int sscanf(const char *buffer, const char *format [,adress,…]); 7.8. Функции ввода-вывода на экран(conio.h) cprintf – осуществляет форматированный вывод на экран. int cprintf(const char *format [,argument,…]); Таблица 7.2 Файлы и их назначение Заголовочный файл
assert.h ctype.h errno.h float.h limits.h locale.h match.h setjmp.h signal.h stdarg.h stddef.h stdio.h stdlib.h string.h time.h
Назначение
Диагностика программ Преобразование и проверка символов Проверка ошибок Работа с числами с плавающей точкой Определение размеров целочисленных типов Поддержка интернациональной среды Математическая библиотека Возможность нелокальных переходов Обработка сигналов Поддержка функций с неопределенным числом параметров Разное Библиотека стандартного ввода-вывода Функции общего назначения Функции работы со строками символов Функции работы с датами и временем
81
getch – читает один символ с консоли без вывода его на экран. int getch(void); getche – cчитывает один символ с консоли и отображает его в текущем текстовом окне экрана. int getche( void ); putch – выводит символ на экран. int putch(int c); Каждая библиотечная функция , определенная стандартом языка С, имеет прототип в соответствующем заголовочном файле. В соответствие со стандартом ANSI языка С должно быть 15 следующих заголовочных файлов (табл. 7.2). На самом деле каждый из компиляторов содержит , как правило больше заголовочных файлов, например – библиотеку функций работы с графическим экраном graphics.h и библиотеку функций для работы с текстовым экраном conio.h.
82
Библиографический список 1. Трой Д. А. Программирование на языке Си для персонального компьютера IBM PC/ Пер.с англ. В. А. Кузьмина; Под ред. И. В. Емелина. М.: Радио и связь, 1991. 429 с. 2. Уинер Р. Язык Турбо Си/ Пер. с англ. М. П. Матенина; Под ред. В. В. Мартынюка. М.: Мир 1991. 384 с. 3. Уэйт М. и др. Язык Си: Руководство для начинающих / М.Уэйт, С. Прата, Д. Мартин; Под ред. Э. А. Трахтенгерца. М.: Мир, 1988. 512 с. 4. Березин Б. И., Березин С. Б. Начальный курс С и С++. М.: ДиалогМИФИ, 1999. 288 с.
83
Оглавление Предисловие ............................................................................................. 1. Основные понятия языка С ................................................................ 1.1. Алфавит, идентификаторы, ключевые слова, комментарии .. 1.2. Примеры простых программ ...................................................... 1.3. Определение некоторых понятий .............................................. 2. Переменные, константы, операции и выражения ........................... 2.1. Базовые типы данных и объявление переменных ................... 2.2. Основная форма объявления переменных ................................ 2.3. Константы .................................................................................... 2.4. Символьные переменные и строки .......................................... 2.5. Инициализация переменных ..................................................... 2.6. Операции ..................................................................................... 2.7. Выражения .................................................................................. 3. Операторы ............................................................................................. 3.1. Условный оператор ..................................................................... 3.2. Оператор множественного выбора ............................................ 3.3. Операторы циклов ....................................................................... 3.4. Операторы break и continue ........................................................ 3.5. Оператор безусловного перехода ............................................... 4. Массивы и указатели ........................................................................... 4.1. Понятие массива, объявление массива ..................................... 4.2. Массивы символов, строки. Функции работы со строками .. 4.3. Двумерные массивы .................................................................... 4.4. Инициализация массивов ........................................................... 4.5. Указатели, объявление указателей, операции над указателями ........................................................................... 4.6. Связь указателей и массивов ..................................................... 4.7. Массивы указателей .................................................................... 5. Функции ................................................................................................ 5.1. Объявление функций. Оператор return .................................... 5.2. Прототипы функций ................................................................... 5.3. Область действия и область видимости ................................... 5.4. Классы памяти ............................................................................. 5.5. Параметры и аргументы функций ............................................. 5.6. Рекурсивные функции ................................................................ 5.7. Указатель на функцию ................................................................ 84
3 4 4 5 8 9 9 11 13 16 18 18 25 27 27 29 32 35 36 37 37 38 41 42 43 47 48 51 51 53 54 56 61 64 65
6. Типы данных, определяемые пользователем ................................... 6.1. Структура .................................................................................... 6.2. Доступ к отдельным битам ....................................................... 6.3. Объединения ............................................................................... 6.4. Перечислимый тип ..................................................................... 6.5. Переименование типов .............................................................. 7. Ввод/вывод и работа с файлами ......................................................... 7.1. Организация ввода-вывода ......................................................... 7.2. Классификация функций чтения и записи ............................. 7.3. Функции библиотеки ввода-вывода .......................................... 7.4. Функции для работы с файлами ................................................ 7.5. Функции неформатированного ввода-вывода ......................... 7.6. Функции блочного ввода-вывода .............................................. 7.7. Функции форматированного ввода-вывода ............................. 7.8. Функции ввода-вывода на экран(conio.h) ................................ Библиографический список ....................................................................
67 67 71 72 74 75 76 76 77 78 78 79 80 81 81 83
85
Учебное издание
Кучин Николай Валентинович Павлова Марина Михайловна
ОСНОВЫ ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ СИ Учебное пособие
Редактор А. В. Семенчук Компьютерная верстка А. Н. Колешко Лицензия ЛР №020341 от 07.05.97. Сдано в набор 11.09.01. Подписано к печати 04.10.01. Формат 60×84 1/16. Бумага тип. №3. Печать офсетная. Усл. печ. л. 4,65. Усл. кр.-отт. 4,77. Уч. -изд. л. 5,0. Тираж 100 экз. Заказ № Редакционно-издательский отдел Лаборатория компьютерно-издательских технологий Отдел оперативной полиграфии СПбГУАП 190000, Санкт-Петербург, ул. Б. Морская, 67