Similar presentations:
L-03.YazikSi.Nachalniesvedeniya
1.
СПбГУТ им. проф. М.А. Бонч-БруевичаКафедра программной инженерии и вычислительной техники (ПИ и ВТ)
ПРОГРАММИРОВАНИЕ
Единственный способ изучать новый язык
программирования - писать на нем программы.
Брайэн Керниган
Лекция 3: Язык Си. Начальные сведения
1. Общая характеристика языка Си.
Структура программы, написанной на языке СИ.
2. Директивы препроцессора.
3. Понятие о функции. Простейшие средства ввода-вывода.
4. Примеры простейших программ, написанных на языке Си (по ходу лекции).
Санкт-Петербург, 2023г.
2.
1. Общая характеристика языка СиПочему Си?
Один из наиболее популярных языков
программирования
Синтаксис языка Си является основой для многих
других языков программирования (С++, Java, JavaScript,
С# и пр.)
Программы на Си хорошо переносимы между
различными платформами (компиляторы Си
существуют, практически, для всех типов процессоров)
Сочетает в себе черты языков низкого и высокого
уровней
Простейшая программа
Адекватной замены этому языку нет и не предвидится, есть такие
классы задач, для которых просто нет других подходящих языков;
придётся, как следствие, «терпеть» Си таким, каков он есть.
Принять этот язык как феномен вам поможет понимание того,
откуда он взялся, почему он именно таков, почему, несмотря на
все свои «выверты» и «выкрутасы», этот язык продолжает
удерживать первое место по популярности и почему, наконец,
программиста, не знающего Си, всегда и везде будут воспринимать
как сотрудника второго сорта, даже если писать на Си от него не
требуется.
2
3.
Рейтинг TIOBE IndexРейтинг TIOBE Index
построен на оценке
результатов поисковых
запросов, содержащих
название языка.
Логика этого индекса
очень проста:
«Если язык ищут в
поисковых системах, то
он популярен».
Интересно то, что C++ ни
разу не смог превысить
по популярности C.
www.tiobe.com/tiobeindex
3
4.
Рейтинг IEEE SpectrumЕжегодный рейтинг IEEE Spectrum Top Programming Language
использует 11 метрик из 8-ми источников, включая поисковые
запросы, упоминания в твитере и даже упоминания в
вакансиях на работу программиста.
С одной стороны этот рейтинг использует больше данных, но
с другой стороны во многих источниках данные имеют
связанный характер.
Чем больше публикуются вакансий на некоторый язык
программирования, тем больше запросов будет в поисковых
системах.
То есть у новых языков больше шансов попасть на вершину
рейтинга.
Важностью особенностью рейтинга IEEE является то, что
рейтинг интерактивный и можно поиграть с параметрами. В
этом рейтинге лидирует Python.
https://spectrum.ieee.org/static/interactive-the-topprogramming-languages-2020
4
5.
Рейтинг Stack OverflowСайт Stack Overflow — это площадка, на которой разработчики могут задавать и отвечать на вопросы по программированию. Этот сайт
имеет около 40 миллионов посещений в месяц. Есть русскоязычная версия сайта: ru.stackoverflow.com
Этот рейтинг рассчитывается на основе опроса разработчиков. В 2020 году было опрошено более 65 000 разработчиков и составлен
рейтинг языков программирования. Скорее это рейтинг языков, которые вызывают вопросы. В этом рейтинге лидером стал JavaScript.
https://insights.stackoverflow.com/survey/2020
Такая популярность вполне объяснима, сейчас JavaScript бурно развивается и каждая новая возможность вызывает массу вопросов,
поэтому программисты идут на сайт Stack Overflow, чтобы задать вопросы.
Любопытно, что в этом рейтинге Cи не попал даже в первую десятку. Видимо, язык настолько прост и понятен, что вопросов не
вызывает.
5
6.
Общая характеристика языка СиИз всей истории языка Си выделяются три основных фактора,
позволяющих понять Си как явление:
во-первых, язык был создан в качестве заменителя языка
ассемблера ;
во-вторых, одним из важнейших соображений при его
создании была простота реализации;
в-третьих, язык был, что называется, «слеплен» под
конкретную задачу, вставшую здесь и сейчас, и делали его,
как говорят в таких случаях, «на коленке».
Вряд ли Дэннис Ритчи, в то время ещё очень молодой, мог
предполагать, что создаваемый им язык переживёт своего
создателя и, уже просуществовав больше сорока лет (а
описываемые события происходили в начале 1970-х годов), всё
ещё не будет выказывать никаких признаков надвигающейся
старости; сейчас можно достаточно смело предсказывать, что
ещё по меньшей мере полтора-два десятка лет языку Си ничего
не грозит, даже если вдруг кому-то удастся создать ему
полноценную замену.
С технологической точки зрения ситуация сейчас выглядит так.
Имеются по меньшей мере две области задач, где единственной
альтернативой Си оказывается язык ассемблера:
это, во-первых, ядра операционных систем;
и, во-вторых, прошивки для микроконтроллеров —
специализированных компьютеров, используемых для
управления техникой (лифтами, стиральными машинами и т.
п.); даже билет на метро представляет собой не что иное, как
компьютер на основе микроконтроллера.
К вопросу о том, почему это так и чем здесь не устраивают
другие языки, мы вернёмся позже, пока просто отметим, что
работа на языке ассемблера оказывается в десятки раз более
трудоёмкой, причём если когда-то давно ещё можно было
ссылаться на более высокую эффективность ассемблерных
программ, то сейчас оптимизаторы кода, встроенные прямо в
компилятор, добиваются таких результатов по быстродействию, что
человек, работая вручную, в большинстве случаев просто не может
сделать лучше.
Поэтому, когда дело доходит до низкоуровневого
программирования, язык ассемблера используется лишь для тех
редких и незначительных по объёму фрагментов, которые не
могут быть сделаны даже на Си ; примером такого фрагмента
может служить точка входа в обработчик прерывания в ядре ОС
или, например, обращение к портам ввода-вывода в драйвере, и
это обычно несколько строчек.
Большую часть низкоуровневой программы пишут именно на
Си, что позволяет экономить дорогостоящее время программистов.
Разработка велась на микроЭВМ
DEC PDP-11:
Оперативная память – 24Кб
Из них используется ОС – 12 Кб
Задачи:
Разработка нового языка
программирования
Разработка на нем
операционной системы
6
7.
Язык Си. Начальные сведенияАлфавит языка
Язык Си был создан уже после внедрения стандарта ASCII,
поэтому использует почти все его символы таблицы ASCII
(нет только $ @ ` ).
в Си есть и круглые (), и квадратные [], и фигурные {}.
в Си различаются заглавные и строчные буквы.
текст, заключённый в служебные символы /* и */ , считается
комментарием.
компиляторы, совместимые со стандартом Cи99, также
позволяют использовать комментарии, начинающиеся с
символов // и заканчивающиеся переводом строки.
В GCC можно использовать // однострочные комментарии
Ключевые слова языка
Ключевые слова ─ это слова, зарезервированные для
специального предназначения и их нельзя использовать как
имена идентификаторов.
auto
double
int
struct
break
else
long
switch
register
typedef
char
extern
return
void
case
float
unsigned
default
for
signed
union
do
if
sizeof
continue
enum
short
while
Лексемы языка
имена (идентификаторы);
ключевые слова;
знаки операций;
разделители;
литералы (константы).
Константы
Константа ─ это ограниченная последовательность символов
алфавита языка, представляющая собой изображение
фиксированного (неизменяемого) объекта.
Константы бывают: логические, числовые, символьные и
строковые.
Числовые константы делятся на целочисленные и
вещественные.
Идентификаторы
Идентификаторы ─ это произвольные имена любой длины
для классов, объектов, функций, переменных, типов данных,
определенных пользователем и т.д.
A…Z и a…z
0…9
Знак _
Ограничения на идентификаторы:
Первый символ – буква или _
Распознаются только первые 32 символа
Регистрозависимый язык
Sum, sum и SUM – разные идентификаторы (имена)
7
8.
Введение. Кодировка ASCII (American Standard Code for Information Interchang)Кодирующая первую половину символов с числовыми кодами от 0 до 127
ASCII — это 7-битовая кодировка.
Ее коды принимают значения от 0000000
до 1111111 или в шестнадцатеричном
выражении от 00h до 7Fh.
Первые 33 кода – управляющие и
непечатные символы (например: ‘\t’ –
09h, ‘\n’ – 0Ah), остальные – буквы
латинского алфавита, цифры и знаки
препинания.
Для кодирования национальных
символов используют расширенную
ASCII-таблицу и коды 128-255.
Для Windows это кодировка Windows CP1251.
Кодировка букв в консоли Windows по
умолчанию MS-DOS CP-866.
Кириллицу также содержат кодовые
страницы ISO-8859-5 (Linux), CP KOI8U и CP KOI-8R (СССР).
Для других языков существуют свои
кодовые таблицы.
(коды от 0 до 32 отведены не символам, а функциональным клавишам).
8
9.
Введение. Фрагмент таблицы кодировки Unicode CyrillicРусскоязычные символы
Юникод имеет несколько форм представления (Unicode transformation format, UTF):
UTF-8, UTF-16 (UTF-16BE, UTF-16LE) и UTF-32 (UTF-32BE, UTF-32LE).
Таблица кодирования символов, которая содержит 1 114 112 кодов для символов всех языков.
Первые 128 символов — с кодами от 0000h до 007Fh — совпадают с символами ASCII.
9
10.
Язык Си. Начальные сведенияКонстанты (продолжение)
Константа
Логическая
Целая
Формат
Обозначается 0 или 1
int или char
Описания идентификаторов
Примеры
false=0
true – любое другое
значение
8, 0, 199226
Десятичный: последовательность
десятичных цифр, начинающаяся не с
нуля, если это не число нуль
Восьмеричный: нуль, за которым
01, 020, 07155
следуют восьмеричные цифры (0, 1,
2, 3, 4, 5, 6, 7)
Шестнадцатеричный: 0х или 0Х, за
0xA, 0x1B8, 0X00FF,
которым следуют
0X00ff
шестнадцатеричные цифры (0, 1, 2, 3,
4, 5, 6, 7, 8, 9, A, B, C, D, E, F)
Вещественная Десятичный: [цифры].[цифры]
5.7, 0.001, 35
Экспоненциальный:
0.2E6, .11e-3, 5E10,
[цифры][.][цифры]{E|e}[+|-][цифры] 1.22E-10
Символьная
Один или более символов,
заключенных в апострофы
'A', 'ю', '*', 'db', 'A',
'\n', '\012', '\x07\x07'
Строковая
Последовательность символов,
заключенная в кавычки
"Здесь был Vasia",
"\tСумма =\xF5\n"
[класс памяти] [const] тип имя [инициализатор];
инициализатор = значение
Примеры:
short int a = 1;
const char C = 'C';
char s, sf = 'f';
char t = 54;
float c = 0.22, x(3), sum;
Переменные
Переменная – именованная ячейка памяти, значение которой
может меняться во время выполнения программы.
Переменная имеет:
значение;
тип – определяет, значения какого типа она может хранить;
адрес в памяти, по которому можно обратиться к значению;
Идентификатор – имя (как правило), по которому можно
обратиться к значению.
Переменные и константы подробно рассмотрим на
следующей лекции.
10
11.
Структура программы на языке СиОтдельного рассмотрения заслуживает выражение
"Hello, world\n"
которое представляет собой строковую константу или,
как часто говорят, строковый литерал.
Следует обратить внимание на двойные кавычки;
апострофы в Си тоже используются, но для других целей.
Кроме того, внимание привлекает конструкция \n;
это обозначение символа перевода строки, то есть
символа с кодом 10.
Можно вспомнить, что в Паскале мы в подобной
ситуации применяли оператор writeln / write:
writeln(’Hello, world’);
write(’Hello, world’#10);
то есть заставляли нашу программу сначала напечатать
данную строку, а затем перевести строку на печати, то
есть фактически напечатать ещё и символ перевода
строки.
В вышеприведённом примере мы просто включили
этот символ в состав печатаемой строки.
Обратим внимание на символ точки с запятой.
В принципе, роль этого символа в языке Си подобна
паскалевской, но есть одно важное отличие:
Если в Паскале точка с запятой разделяла операторы,
то в Си точка с запятой является частью синтаксиса
оператора.
#include <stdio.h>
int main (void)
{
printf ("Hello, World!\n");
return 0;
}
Рассмотрим теперь следующую строку, «return 0;».
Слово return в переводе с английского означает
«возврат»; в языке Си это оператор возврата из функции.
Он делает две вещи:
во-первых, работа функции на этом заканчивается,
что можно, например, использовать для досрочного
её завершения (подобно паскалевскому exit);
во-вторых, его параметр — выражение, написанное
после слова return, в данном случае 0 — задаёт то
значение, которое вернёт функция (вспомним, что
наша функция main описана как возвращающая
целое число).
В данном случае наша функция main вернёт число 0.
Значение, возвращаемое из main, предназначается ОС, а
0 в данном случае означает, что всё в порядке, т. е.
программа, завершаясь, считает, что возложенную на неё
миссию успешно выполнила.
11
12.
Структура программы на языке СиНам осталось рассмотреть, пожалуй, самую
заковыристую строку в программе:
директиву #include <stdio.h>
Как уже, несомненно, догадались, эта директива в
тексте программы заменяет сама себя на полное
содержимое файла stdio.h, а нужно это, чтобы
компилятор узнал про функцию printf — как уже
говорилось, сам по себе он о ней не знает.
Здесь следует отметить сразу несколько интересных
моментов:
Обращают на себя внимание используемые угловые
скобки <>, которые означают, что включаемый файл
следует искать в системных директориях;
точнее говоря, считается, что именно так следует
включать любой файл, который не является сам по себе
частью нашей программы;
когда же мы пишем программу, состоящую из многих
файлов, то для включения своих собственных файлов
мы используем #include с параметром в двойных
кавычках “ “, например:
#include "mymodule.h"
файл stdio.h — это вполне реальный файл; скорее всего,
он находится в вашей системе в директории
/usr/include, так что вы можете просмотреть его,
например, командой less:
less /usr/include/stdio.h
#include <stdio.h>
int main (void)
{
printf ("Hello, World!\n");
return 0;
}
Важно понимать, что в этом файле содержится только
заголовок функции printf, а самой функции там нет;
отсюда используемый суффикс «.h», от слова header, то
есть «заголовок» — в английской терминологии это
называется header file, а по-русски — «заголовочный
файл».
Всё, что компилятор узнает, увидев заголовок — это что
где-то (и притом совершенно неважно, где) есть функция
с таким-то именем, принимающая столько-то параметров
таких-то типов; как функция выглядит, что она делает —
этого компилятору знать не нужно.
Дело в том, что компилятор генерирует не готовый
машинный код, а объектный модуль, в котором пока что
не хватает некоторых адресов; в данном случае
результатом работы компилятора становится модуль, не
содержащий самой функции printf.
Вместо неё модуль содержит указание на то, что
редактору связей (линкеру) следует откуда-то добыть
функцию с таким именем, а её адрес подставить куда
следует.
Иначе говоря, реальная функция printf появится в нашей
программе только на этапе окончательной сборки, когда
работа компилятора будет завершена.
12
13.
Текстпрограммы
Транслятор
(компилятор)
Компиляция и интерпретация
ЯВУ не зависит от внутренних машинных языков
Технология компиляции
целевых процессоров, поэтому программы, написанные
на языках высокого уровня, требуют перевода в
Файл
Файл
Текстовый
Препромашинные коды с помощью специальных программ –
(ИТ)
(ИМ)
редактор
цессор
трансляторов (компиляторов или
интерпретаторов), т.е. языки программирования
могут быть реализованы как компилируемые и
интерпретируемые.
Программа на компилируемом языке при помощи
компилятора преобразуется (компилируется) в набор
Файл
Файл
Линкер
Загрузчик
инструкций для данного типа процессора (машинный
(ОМ)
(компонов(ИМ)
щик)
код – двоичные коды инструкций конкретного
…
процессора), который далее преобразуется
(компонуется) в исполнимый модуль.
Файл
(ОМ)
Технология интерпретации
Исходные
данные
Исполняемая
программа
Результаты
Текст
программы
Транслятор
(интерпретатор)
Программу на интерпретируемом языке
непосредственно выполняет без предварительного
перевода программа-интерпретатор.
Результаты
Исходные
данные
13
14.
Структура программы на языке СиТеперь, когда мы закончили разбор программы «Hello,
world», самое время попробовать её запустить.
Итак, включаем компьютер, входим в систему,
запускаем текстовый редактор Vim (или Nano, или MC
Edit); учтите, что ваш файл должен иметь суффикс «.c»,
поскольку вы собираетесь в нём набрать программу на
Си.
Например, имя hello.c для вашего файла вполне
подойдёт.
Набираем ровно такой текст программы, как показано
на предыдущих слайдах, и сохраняем его.
Теперь нам нужно запустить компилятор, который в
данном случае называется gcc.
Компилятор gcc поддерживает множество
разнообразных флажков командной строки, из которых
нам потребуются по меньшей мере два:
-Wall, который включает все разумные
предупреждения,
и -g, который заставляет компилятор сгенерировать
отладочную информацию.
Эти флажки мы при запуске gcc указываем всегда, то
есть вообще всегда, следует выработать привычку к этому
на уровне, как говорят, спинного мозга: отсутствие любого
из этих флажков может очень дорого обойтись.
Не указывать эти флаги можно разве что тогда, когда
компилятор запускает не программист, а конечный
пользователь, чтобы из присланных ему исходных текстов
получить исполняемую программу.
В этом случае предполагается, что программа уже
полностью готова и отлажена, а даже если она и
содержит ошибки, то конечный пользователь всё равно
не станет их исправлять. Но это ситуация не наша: мы
пользуемся компилятором как инструментом
программиста.
Ещё один полезный флаг, -o, позволяет задать имя
файла, в который будет записан результат компиляции.
Если этот флажок не указать, итоговый исполняемый
файл будет иметь имя a.out, что не всегда удобно.
Как мы помним, обычно исполняемые файлы в ОС Unix
не имеют расширения(суффикса).
Именно так мы поступим и сейчас — отправим
результат компиляции в файл hello:
…$ gcc -Wall -g -o hello hello.c
Полностью эта команда означает следующее:
«Возьми исходный файл hello.c, откомпилируй его,
выдавая при этом все разумные предупреждения,
добавь в полученный модуль отладочную информацию,
потом вызови редактор связей, «скормив» ему наш
модуль и стандартную библиотеку языка Си (!), а
результат пусть он запишет в файл hello».
Промежуточный объектный файл нам в этот раз не
нужен, поскольку наша программа состоит из одного
модуля; компилятор поместит объектный код во
временный файл, который по завершении работы сотрёт.14
15.
Интерфейс командной строки ─ CLI (Command-line interface)Вы можете запустить сеанс ОС Linux или другие
операционные системы в Вашем браузере.
Пример терминала ОС Linux JSLinux:
https://bellard.org/jslinux/
JSLinux - это более полный эмулятор Gnu /
Linux, который предлагает не только терминал.
Как видно из названия, он полностью написан на
JavaScript.
Мы сможем выбрать консольную систему или
онлайн-систему на основе графического
интерфейса.
JSLinux также позволит нам загружать файлы на
виртуальную машину.
15
16.
Процесс в ОПС точки зрения ОС Linux клавиатура и экран – это файлы.
Есть три стандартные константы – три стандартных заранее
открытых файла:
FILE *stdin
// стандартный файл (поток) ввода
stdin – клавиатура
FILE *stdout // стандартный файл (поток) вывода
FILE *stderr
// стандартный файл (поток) ошибок
stdout и stderr – монитор
Для ввода и вывода символа применяются функции getchar()
и putchar(). Но следует сказать, что на самом деле они
полноценными функциями не являются, а определены как
макросы в заголовочном файле stdio.h:
#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
stderr – это стандартный файл (поток) ошибок.
По умолчанию выводит данные на экран.
Но существует заметное отличие этого "файла" от stdin и
stdout: stderr – небуферизованный файл (поток).
Поэтому в этот файл (поток) все байты уходят без "задержки",
которая могла бы возникнуть при буферизированном подходе.
Польза от этого подхода заключается в том, что вместо кода:
fprintf(stdout, ...);
fflush(stdout);
мы пишем:
Программа
на диске
(файл)
Загрузка в ОП
Процесс
в памяти
Файл — это
логический
набор данных
+ Стек (1Мбайт)
+ Куча/heap/БДП
(1Мбайт)
0 ─ stdin
1 ─ stdout
Выделяются
процессу в ОП
2 ─ stderr
Номера дескрипторов
(описателей) открытых
файлов (fd)
fprintf(stderr, ...);
16
17.
2. Директивы препроцессораЕсли всё сделать правильно, компилятор отработает
Препроцессором называется программа, которая
полностью молча, не выдав ни слова, а в текущей
вызывается компилятором и обрабатывает
директории появится файл hello, который можно будет
исходную программу перед ее компиляцией.
запустить:
Препроцессор различает специальные команды –
avst@host:~/work$ ./hello
директивы.
Hello, world
Препроцессор обеспечивает подключение текстов
avst@host:~/work$
исходных модулей, формирование макроопределений,
планирование условной компиляции.
Директивы препроцессора
Препроцессорные директивы начинаются с символа #,
В языке Си присутствует концепция макропроцессора
за которым следует наименование директивы,
и, более того, мы с самого начала используем его во всех
указывающее текущую операцию препроцессора.
наших примерах программ, хотя и не обращаем на это
#include < ID_файла.h>
особого внимания.
Дело в том, что хорошо знакомая нам директива
#include <stdio.h> – вставляет в программу текст из
#include как раз является частью макропроцессора.
файла stdio.h, который находится в стандартных
Общая идея макропроцессирования состоит в том, что
каталогах (указанных компилятору, т.е. заданных в среде
текст (в данном случае текст программы) в какой-то
разработки).
момент подаётся на вход специальной программе или
Примеры файлов *.h:
подсистеме компилятора, которая называется
stdio.h – содержит стандартные функции файлового
макропроцессором.
ввода-вывода;
Текст, подаваемый на вход макропроцессору, как
conio.h – функции для работы с консолью (клавиатура,
правило, содержит определённые указания по его
экран монитора);
преобразованию, выраженные в виде макродиректив и
math.h – математические функции.
макровызовов.
Результатом работы макропроцессора становится
опять-таки текст, но уже не содержащий макродиректив
#include "My_file.h"
и не требующий дальнейшего преобразования.
Вставляет в программу текст из файла My_file.h,
который находится в текущем каталоге проекта.
17
18.
Директивы препроцессора. Макроопределения и макровызовыВторое основное назначение препроцессора –
обработка макроопределений.
Макроподстановка имеет общий вид:
#define < ID > <строка>
Например: #define PI 3.1415927
В ходе препроцессорной обработки программы
появление в тексте идентификатора PI везде заменяется
значением 3.1415927.
Макроопределение представляет собой фрагмент
исходного текста программы, в котором вводится новое
имя (идентификатор), предназначенное к обработке
макропроцессором.
Примеры:
#define BUFFERSIZE 1024
#define HELLOMSG "Hello, world\n"
#define MALLOCITEM malloc(sizeof(struct item))
Здесь BUFFERSIZE, HELLOMSG и MALLOCITEM —
макроимена или просто макросы; встретив любой из
этих идентификаторов в дальнейшем тексте программы,
компилятор заменит первый из них на число 1024, второй
— на строковую константу "Hello, world\n", третий — на
вызов функции malloc.
Можно ожидать теперь появления в программе чего-то
вроде следующего:
char buffer[BUFFERSIZE];
/* ... */
puts(HELLOMSG);
/* ... */
struct item *p = MALLOCITEM;
и так далее.
Это, собственно говоря, и есть макровызовы.
Тело макроса не обязано быть законченным
выражением или даже законченной конструкцией;
вполне можно описать, например, такое:
#define IF if(
#define THEN ) {
#define ELSE } else {
#define FI }
и затем в программе изобразить что-то вроде
IF a > b THEN
printf("the first was greater then the second\n"); b = a;
ELSE
printf("the second was greater\n"); a = b;
FI
Иной вопрос, что конкретно вот так делать всё же не
стоит (хотя и можно), но в более сложных случаях
подобный синтез языковых конструкций из макросов
вполне может себя оправдать.
18
19.
3. Понятие о функцииПодпрограмма (ПП) – это поименованный или иным
образом идентифицированный фрагмент
компьютерной программы, которому можно
передать управление (вызвать) в любой её точке и
который имеет возможность вернуть управление в
точку, следующую за точкой своего вызова
Ранее (60-е годы ХХ века): Уменьшение размера
памяти, занимаемой кодом программы – почти
неактуально в наст. вр.
Структуризация программы с целью удобства её
понимания и сопровождения
Исправление ошибок, оптимизация, расширение
функциональности в ПП автоматически отражается на
всех её вызовах
Вынесение в ПП даже однократно выполняемого
набора действий делает программу более понятной и
обозримой
Механизм вызова ПП:
Вызов ПП делится на
Подготовительные служебные действия
вызывающей программы
Собственно работу ПП
Заключительные служебные действия
вызывающей программы
Механизм вызова ПП (продолжение):
Каждому вызову ПП соответствует отдельная область
памяти – стековый кадр
Функция — это самостоятельная единица
программы, которая спроектирована для реализации
конкретной подзадачи.
Функция является подпрограммой, которая может
содержаться в основной программе, а может быть создана
отдельно (в библиотеке). Каждая функция выполняет в
программе определенные действия.
Сигнатура функции определяет правила использования
функции. Обычно сигнатура представляет собой описание
функции, включающее имя функции, перечень формальных
параметров с их типами и тип возвращаемого значения.
Семантика функции определяет способ реализации
функции. Обычно представляет собой тело функции.
Определение функции
Каждая функция в языке Си должна быть определена, то
есть должны быть указаны:
тип возвращаемого значения;
имя функции;
информация о формальных аргументах;
тело функции.
19
20.
Понятие о функцииРазличают:
Определение функции имеет следующий синтаксис:
системные (в составе систем программирования)
Тип ИмяФункции(СписокФормальныхАргументов)
собственные функции.
{
Системные функции хранятся в стандартных
ТелоФункции;
библиотеках, и пользователю не нужно вдаваться в
...
подробности их реализации. Достаточно знать
return(ВозвращаемоеЗначение);
лишь их сигнатуру.
}
Примером системных функций, используемых ранее,
являются функции printf() и scanf().
// Пример: Функция сложения двух вещественных чисел
Собственные функции — это функции, написанные
float function(float x, float z)
пользователем для решения конкретной подзадачи.
{
Разбиение программ на функции дает следующие
float y;
преимущества:
y=x+z;
Функцию можно вызвать из различных мест программы,
return(y);
что позволяет избежать повторения программного кода.
}
Одну и ту же функцию можно использовать в разных
В указанном примере возвращаемое значение имеет
тип float.
В качестве возвращаемого значения в вызывающую
функцию передается значение переменной y.
Формальными аргументами являются значения
переменных x и z.
Если функция не возвращает значения, то тип
возвращаемого значения для нее указывается как void.
При этом операция return может быть опущена.
Если функция не принимает аргументов, в круглых
скобках также указывается void.
программах.
Функции повышают уровень модульности программы и
облегчают ее проектирование.
Использование функций облегчает чтение и понимание
программы и ускоряет поиск и исправление ошибок.
С точки зрения вызывающей программы функцию можно
представить как некий «черный ящик», у которого есть
несколько входов и один выход.
В языке Си нельзя определять одну функцию внутри
другой.
Функции могут определяться как до вызывающей
функции, так и после нее.
20
21.
Понятие о функцииВозврат в вызывающую функцию
По окончании выполнения вызываемой функции
осуществляется возврат значения в точку ее вызова.
Это значение присваивается переменной, тип которой
должен соответствовать типу возвращаемого значения
функции.
Функция может передать в вызывающую программу
только одно значение.
Для передачи возвращаемого значения в вызывающую
функцию используется оператор return в одной из форм:
return(ВозвращаемоеЗначение);
return ВозвращаемоеЗначение;
Действие оператора следующее: значение выражения,
заключенного в скобки, вычисляется и передается в
вызывающую функцию.
Возвращаемое значение может использоваться в
вызывающей программе как часть некоторого выражения.
Оператор return также завершает выполнение функции и
передает управление следующему оператору в
вызывающей функции.
Оператор return не обязательно должен находиться в
конце тела функции.
Функции могут и не возвращать значения, а просто
выполнять некоторые вычисления.
В этом случае указывается пустой тип возвращаемого
значения void, а оператор return может либо отсутствовать,
либо не возвращать никакого значения:
return;
Пример: Посчитать сумму двух чисел.
#define _CRT_SECURE_NO_WARNINGS /* для возможности
использования scanf */
#include <stdio.h>
// Функция вычисления суммы двух чисел
int sum(int x, int y) /* в функцию передаются два целых
числа */
{
int k = x + y; // вычисляем сумму чисел и сохраняем в k
return k;
// возвращаем значение k
}
int main()
{
int a, r; // описание двух целых переменных
printf("a= ");
scanf("%d", &a); // вводим a, %d (десятичное целое)
r = sum(a, 5); // вызов функции: x=a, y=5
printf("%d + 5 = %d", a, r); // вывод: a + 5 = r
getchar(); getchar(); // мы использовали scanf(),
return 0; // поэтому getchar() вызываем дважды
}
21
22.
Простейшие средства ввода-выводаОсновной задачей программирования является обработка
информации, поэтому любой язык программирования имеет
средства для ввода и вывода информации.
В языке Си нет операторов ввода-вывода.
Ввод и вывод информации осуществляется через функции
стандартной библиотеки.
Прототипы рассматриваемых функций находятся в файле
stdio.h.
Эта библиотека содержит функции:
printf() — для вывода информации
scanf() — для ввода информации.
Вывод информации
Функция printf() предназначена для форматированного
вывода.
Она переводит данные в символьное представление и выводит
полученные изображения символов на экран.
При этом у программиста имеется возможность
форматировать данные, то есть влиять на их представление на
экране.
Общая форма записи функции printf():
printf("Строка_Форматов", объект1, объект2, ..., объектn);
Строка_Форматов состоит из следующих элементов:
управляющих символов;
текста, представленного для непосредственного вывода;
форматов, предназначенных для вывода значений
переменных различных типов.
Объекты могут отсутствовать.
Управляющие символы не выводятся на экран, а управляют
расположением выводимых символов.
Отличительной чертой управляющего символа является
наличие обратного слэша ‘\’ перед ним.
Пример основных управляющих символов:
‘\n’ — перевод строки;
‘\t’ — горизонтальная табуляция;
‘\v’ — вертикальная табуляция;
‘\b’ — возврат на символ;
‘\r’ — возврат на начало строки;
‘\a’ — звуковой сигнал.
Форматы нужны для того, чтобы указывать вид, в котором
информация будет выведена на экран.
Отличительной чертой формата является наличие символа
процент ‘%’ перед ним:
%d — целое число типа int со знаком в десятичной системе
счисления;
%u — целое число типа unsigned int;
%x — целое число типа int со знаком в шестнадцатеричной
системе счисления;
%o — целое число типа int со знаком в восьмеричной системе
счисления;
%hd — целое число типа short со знаком в десятичной системе
счисления;
%hu — целое число типа unsigned short;
%hx — целое число типа short со знаком в шестнадцатеричной
системе счисления;
%ld — целое число типа long int со знаком в десятичной
системе счисления;
%lu — целое число типа unsigned long int;
22
23.
5. Простейшие средства ввода-выводаФорматы нужны для того, чтобы указывать вид, в котором
информация будет выведена на экран. Отличительной чертой
формата является наличие символа процент ‘%’ перед ним:
%lx — целое число типа long int со знаком в
шестнадцатеричной системе счисления;
%f — вещественный формат (числа с плавающей точкой типа
float);
%lf — вещественный формат двойной точности (числа с
плавающей точкой типа double);
%e — вещественный формат в экспоненциальной форме
(числа с плавающей точкой типа float в экспоненциальной
форме);
%c — символьный формат;
%s — строковый формат.
Строка форматов содержит форматы для вывода значений.
Каждый формат вывода начинается с символа %.
После строки форматов через запятую указываются имена
переменных, которые необходимо вывести.
Количество символов % в строке формата должно совпадать с
количеством переменных для вывода.
Тип каждого формата должен совпадать с типом переменной,
которая будет выводиться на это место.
Замещение форматов вывода значениями переменных
происходит в порядке их следования.
#include <stdio.h>
int main()
{
int a = 5;
float x = 2.78;
printf("a=%d\n", a);
printf("x=%f\n", x);
getchar();
return 0;
}
То же самое с использованием одного вызова printf:
#include <stdio.h>
int main()
{
int a = 5;
float x = 2.78;
printf("a=%d\nx=%f\n", a, x);
getchar();
return 0;
}
23
24.
Табличный выводПростейшие средства ввода-вывода
Ввод информации
При указании формата можно явным образом указать общее
количество знакомест и количество знакомест, занимаемых
дробной частью:
#include <stdio.h>
int main()
{
float x = 1.2345;
printf("x=%10.5f\n", x);
getchar();
return 0;
}
В приведенном примере:
10 — общее количество знакомест, отводимое под значение
переменной;
5 — количество позиций после разделителя целой и
дробной части (после десятичной точки).
В указанном примере количество знакомест в выводимом
числе меньше 10, поэтому свободные знакоместа слева от
числа заполняются пробелами.
Такой способ форматирования часто используется для
построения таблиц.
Функция форматированного ввода данных с клавиатуры
scanf() выполняет чтение данных, вводимых с клавиатуры,
преобразует их во внутренний формат и передает
вызывающей функции.
При этом программист задает правила интерпретации
входных данных с помощью спецификаций форматной
строки.
Общая форма записи функции scanf( ):
scanf ("CтрокаФорматов", адрес1, адрес2,...);
Строка форматов аналогична функции printf().
Для формирования адреса переменной используется символ
амперсанд ‘&’:
адрес = &объект
Строка форматов и список аргументов для функции
обязательны.
#define _CRT_SECURE_NO_WARNINGS /* для возможности
использования scanf */
#include <stdio.h>
#include <stdlib.h> // для перехода на русский язык
int main()
{
float y;
printf("Введите y: "); // выводим сообщение
scanf("%f", &y); // вводим значения переменной y
printf("Значение переменной y=%f", y); /* выводим значение
переменной y */
getchar(); getchar();
return 0;
}
24
25.
Простейшие средства ввода-выводаФункция scanf( ) является функцией незащищенного ввода,
т.к. появилась она в ранних версиях языка Си.
Поэтому чтобы разрешить работу данной функции в
современных компиляторах необходимо в начало программы
добавить строчку
#define _CRT_SECURE_NO_WARNINGS
Другой вариант — воспользоваться функцией защищенного
ввода scanf_s( ), которая появилась несколько позже, но
содержит тот же самый список параметров.
#include <stdio.h>
int main()
{
int a;
printf("a = ");
scanf_s("%d", &a);
printf("a = %d",a);
getchar(); getchar();
return 0;
}
25
26.
Справочно: Форматированный вывод данных. Функция printf( )Функция printf( ) (прототип содержится в файле stdio.h)
обеспечивает форматированный вывод.
Ее можно записать в следующем формальном виде:
рrintf ("управляющая строка", аргумент _1,
аргумент _2,...);
Управляющая строка содержит компоненты трех
типов:
1) обычные символы, которые просто копируются в
стандартный выходной поток (выводятся на экран
дисплея);
2) спецификации преобразования, каждая из которых
вызывает вывод на экран очередного аргумента из
последующего списка;
3) управляющие символьные константы.
Каждая спецификация преобразования начинается со
знака % и заканчивается некоторым символом, задающим
преобразование.
Между знаком % и символом преобразования могут
встречаться другие знаки в соответствии со следующим
форматом:
% [признаки] [ширина_поля] [точность] [F|N|h|l|L] c_n
Все параметры в квадратных скобках не являются
обязательными.
На месте параметра c_n (символ преобразования) могут быть
записаны:
с - значением аргумента является символ;
d или i - значением аргумента является десятичное целое
число;
е - значением аргумента является вещественное десятичное
число в экспоненциальной форме вида 1.23e+2;
Е - значением аргумента является вещественное десятичное
число в экспоненциальной форме вида 1.23E+2;
f - значением аргумента является вещественное десятичное
число с плавающей точкой;
g (или G) - используется, как е или f, и исключает вывод
незначащих нулей;
о - значением аргумента является восьмеричное целое число;
s - значением аргумента является строка символов (символы
строки выводятся до тех пор, пока не встретится символ конца
строки или же не будет, выведено число символов, заданное
точностью);
u - значением аргумента является беззнаковое целое число;
х - значением аргумента является шестнадцатеричное целое
число с цифрами 0,..., 9, а, b, с, d, е, f;
X - значением аргумента является шестнадцатеричное целое
число с цифрами 0,..., 9, А, В, С, О, Е, F;
р - значением аргумента является указатель;
n - применяется в операциях форматирования. Аргумент,
соответствующий этому символу спецификации, должен быть
указателем на целое. В него возвращается номер позиции строки
(отображаемой на экране), в которой записана спецификация %n.
26
27.
Справочно: Форматированный ввод данных. Функция scanf( )f - на входе ожидается вещественное число с плавающей
точкой;
обеспечивает форматированный ввод.
g или G - на входе ожидается вещественное число с
Ее можно записать в следующем формальном виде:
плавающей точкой;
о - на входе ожидается восьмеричное целое число и аргумент
scanf("управляющая строка", аргумент_1, аргумент_2,...); является указателем на переменную типа int;
О - на входе ожидается восьмеричное целое число и аргумент
является
указателем на переменную типа long;
Аргументы scanf( ) должны быть указателями на соответствующие
s
на
входе
ожидается появление строки символов;
значения.
х - на входе ожидается шестнадцатеричное целое число и
Для этого перед именем переменной записывается символ &.
аргумент является указателем на переменную типа int;
Назначение указателей будет рассмотрено далее.
Х - на входе ожидается шестнадцатеричное целое число и
Управляющая строка содержит спецификации преобразования и
аргумент является указателем на переменную типа long;
р - на входе ожидается появление указателя в виде
используется для установления количества и типов аргументов. В нее
шестнадцатеричного числа;
могут включаться:
n - применяется в операциях форматирования. Аргумент,
пробелы, символы табуляции и перехода на новую строку (все они
соответствующий
этому символу спецификации, должен быть
игнорируются);
указателем на целое. В него возвращается номер позиции (после
спецификации преобразования, состоящие из знака %, возможно, ввода), в которой записана спецификация %n;
символа * (запрещение присваивания), возможно, числа,
u - на входе ожидается беззнаковое целое число и аргумент
задающего максимальный размер поля, и самого символа
является указателем на переменную типа unsigned int;
U - на входе ожидается беззнаковое целое число и аргумент
преобразования;
является
указателем на переменную типа unsigned long;
обычные символы, кроме % (считается, что они должны совпадать
[ ] - сканирует входную строку для получения символов.
с очередными неизвестными символами во входном потоке).
Перед некоторыми символами преобразования могут
записываться
следующие модификаторы:
Рассмотрим символы преобразования функции scanf( )
F
изменяет
указатель,
заданный по умолчанию, на указатель
(указываются после символа %):
типа far;
с - на входе ожидается появление одиночного символа;
N - изменяет указатель, заданный по умолчанию, на указатель
d или i - на входе ожидается десятичное целое число и аргумент
типа near;
h - преобразует аргумент к типу short int (может записываться
является указателем на переменную типа int;
перед символами d, i, о, u, х);
D или l - на входе ожидается десятичное целое число и аргумент
l - преобразует аргумент к типу long int (может записываться
является указателем на переменную типа long;
перед символами d, i, o, u, x);
е или Е - на входе ожидается вещественное число с плавающей
L - преобразует аргумент к типу long double (может записываться
точкой;
перед символами е, f, g).
Функция scanf( ) (прототип содержится в файле stdio.h)
27
programming