Язык программирования СИ

Содержание

Слайд 2

Язык программирования СИ

Си (англ. C) — компилируемый статически типизированный язык программирования общего назначения, разработанный в 1969—1973 годах сотрудником Bell Labs Деннисом

Язык программирования СИ Си (англ. C) — компилируемый статически типизированный язык программирования
Ритчи как развитие языка Би. Первоначально был разработан для реализации операционной системы UNIX, но впоследствии былперенесён на множество других платформ. Согласно дизайну языка, его конструкции близко сопоставляются типичным машинным инструкциям, благодаря чему он нашёл применение в проектах, для которых был свойственен язык ассемблера, в том числе как в операционных системах, так и в различном прикладном программном обеспечении для множества устройств — от суперкомпьютеров до встраиваемых систем. Язык программирования Си оказал существенное влияние на развитие индустрии программного обеспечения, а его синтаксис стал основой для таких языков программирования, как C++, C#, Java и Objective-C.

Слайд 3

Обзор
Язык программирования Си отличается минимализмом. Авторы языка хотели, чтобы программы на

Обзор Язык программирования Си отличается минимализмом. Авторы языка хотели, чтобы программы на
нём легко компилировались с помощью однопроходного компилятора, чтобы каждой элементарной составляющей программы после компиляции соответствовало весьма небольшое число машинных команд, а использование базовых элементов языка не задействовало библиотеку времени выполнения. Однопроходный компилятор компилирует программу, не возвращаясь назад, к уже обработанному тексту. Поэтому использованию функции и переменных должно предшествовать их объявление. Код на Си можно легко писать на низком уровне абстракции, почти как на ассемблере. Иногда Си называют «универсальным ассемблером» или «ассемблером высокого уровня», что отражает различие языков ассемблера для разных платформ и единство стандарта Си, код которого может быть скомпилирован без изменений практически на любой модели компьютера. Си часто называют языком среднего уровня или даже низкого уровня, учитывая то, как близко он работает к реальным устройствам. Однако, в строгой классификации, он является языком высокого уровня.

Слайд 4

Ранние разработки
Язык программирования Си был разработан в лабораториях Bell Labs в период

Ранние разработки Язык программирования Си был разработан в лабораториях Bell Labs в
с 1969 по 1973 годы. Согласно Ритчи, самый активный период творчества пришёлся на 1972 год. Язык назвали «Си» (C — третья буква латинского алфавита), потому что многие его особенности берут начало от старого языка «Би» (B — вторая буква латинского алфавита).
Самый первый компьютер, для которого была первоначально написана UNIX, предназначался для создания системы автоматического заполнения документов. Первая версия UNIX была написана на ассемблере. Позднее для того, чтобы переписать эту операционную систему, был разработан язык Си.
К 1973 году язык Си стал достаточно силён, и большая часть ядра UNIX, первоначально написанная на ассемблере PDP-11/20, была переписана на Си. Это было одно из самых первых ядер операционных систем, написанное на языке, отличном от ассемблера

Слайд 5

K&R C
В 1978 году Ритчи и Керниган опубликовали первую редакцию книги «Язык

K&R C В 1978 году Ритчи и Керниган опубликовали первую редакцию книги
программирования Си». Эта книга, известная среди программистов как «K&R», служила многие годы неформальной спецификацией языка. Версию языка Си, описанную в ней, часто называют «K&R C». (Вторая редакция этой книги посвящена более позднему стандарту ANSI C, описанному ниже.)
K&R C часто считают самой главной частью языка, которую должен поддерживать компилятор Си. Многие годы даже после выхода ANSI C, он считался минимальным уровнем, которого следовало придерживаться программистам, желающим добиться от своих программ максимальной портативности, потому что не все компиляторы тогда поддерживали ANSI C, а хороший код на K&R C был верен и для ANSI C.

Слайд 6

ISO C ANSI C
В конце 1970-х годов Си начал вытеснять Бейсик с

ISO C ANSI C В конце 1970-х годов Си начал вытеснять Бейсик
позиции ведущего языка для программирования микрокомпьютеров. В 1980-х годах он был адаптирован для использования в IBM PC, что привело к резкому росту его популярности. В то же время Бьярне Строуструп и другие в лабораториях Bell Labs начали работу по добавлению в Си возможностей объектно-ориентированного программирования. Язык, который они в итоге сделали, C++, в настоящее время является самым распространённым языком программирования. Си остаётся более популярным в UNIX-подобных системах.
В 1983 году Американский Национальный Институт Стандартизации (ANSI) сформировал комитет для разработки стандартной спецификации Си. По окончании этого долгого и сложного процесса в 1989 году он был наконец утверждён как «Язык программирования Си» ANSI X3.159-1989. Эту версию языка принято называть ANSI C или C89. В 1990 году стандарт ANSI C был принят с небольшими изменениями Международной Организацией по Стандартизации (ISO) как ISO/IEC 9899:1990.
Одной из целей этого стандарта была разработка надмножества K&R C, включающего многие особенности языка, созданные позднее. Однако комитет по стандартизации также включил в него и несколько новых возможностей, таких как прототипы функций (заимствованные из С++) и более сложный препроцессор.
ANSI C сейчас поддерживают почти все существующие компиляторы. Почти весь код Си, написанный в последнее время, соответствует ANSI C. Любая программа, написанная только на стандартном Си, гарантированно будет правильно выполняться на любой платформе, имеющей соответствующую реализацию Си. Однако большинство программ написаны так, что они будут компилироваться и исполняться только на определённой платформе, потому, что:
они используют нестандартные библиотеки, например, для графических дисплеев;
они используют специфические платформо-зависимые средства;
они рассчитаны на определённое значение размера некоторых типов данных или на определённый способ хранения этих данных в памяти для конкретной платформы.

Слайд 7

C99
После стандартизации в ANSI спецификация языка Си оставалась относительно неизменной в течение

C99 После стандартизации в ANSI спецификация языка Си оставалась относительно неизменной в
долгого времени, в то время как Си++ продолжал развиваться (в 1995 году в стандарт Си была внесена Первая нормативная поправка, но её почти никто не признавал). Однако в конце 1990-х годов стандарт подвергся пересмотру, что привело к публикации ISO 9899:1999 в 1999 году. Этот стандарт обычно называют «С99». В марте 2000 года он был принят и адаптирован ANSI.
Вот некоторые новые особенности С99:
подставляемые функции (inline);
отсутствие ограничений на место объявления локальных переменных (как и в С++);
новые типы данных, такие как long long int (для облегчения перехода от 32- к 64-битным числам), явный булевый тип данных _Bool и тип complex для представления комплексных чисел;
массивы переменной длины;
поддержка ограниченных указателей (restrict);
именованная инициализация структур: struct { int x, y, z; } point = { .y=10, .z=20, .x=30 };
поддержка однострочных комментариев, начинающихся на //, заимствованных из C++ (многие компиляторы Си поддерживали их и ранее в качестве дополнения);
несколько новых библиотечных функций, таких как snprintf;
несколько новых заголовочных файлов, таких как stdint.h.
Интерес к поддержке новых особенностей С99 в настоящее время смешан. В то время как GCC[2], компилятор Си от Sun Microsystems и некоторые другие компиляторы в настоящее время поддерживают большую часть новых особенностей С99, компиляторы компаний Borland и Microsoft не делают этого, причём похоже, что две эти компании и не думают их добавлять.

Слайд 8

C11

8 декабря 2011 опубликован новый стандарт для языка Си (ISO/IEC 9899:2011). Основные

C11 8 декабря 2011 опубликован новый стандарт для языка Си (ISO/IEC 9899:2011).
изменения:
поддержка многопоточности;
улучшенная поддержка Юникода;
обобщённые макросы (type-generic expressions, позволяют статичную перегрузку);
анонимные структуры и объединения (упрощают обращение ко вложенным конструкциям);
управление выравниванием объектов;
статичные утверждения (static assertions);
удаление опасной функции gets (в пользу безопасной gets_s);
функция quick_exit;
спецификатор функции _Noreturn;
новый режим эксклюзивного открытия файла.

Слайд 9

Связь с C++
Язык программирования С++ произошёл от Си. Однако в дальнейшем Си

Связь с C++ Язык программирования С++ произошёл от Си. Однако в дальнейшем
и C++ развивались независимо, что привело к росту несовместимостей между ними. Последняя редакция Си, С99, добавила в язык несколько конфликтующих с С++ особенностей. Эти различия затрудняют написание программ и библиотек, которые могли бы нормально компилироваться и работать одинаково в компиляторах Си и C++, что, конечно, запутывает тех, кто программирует на обоих языках.
Бьёрн Страуструп, придумавший С++, неоднократно выступал за максимальное сокращение различий между Си и C++ для создания максимальной совместимости между этими языками. Противники же такой точки зрения считают, что так как Си и C++ являются двумя различными языками, то и совместимость между ними не так важна, хоть и полезна. Согласно этому лагерю, усилия по уменьшению несовместимости между ними не должны препятствовать попыткам улучшения каждого языка в отдельности.

Слайд 10

C++
C++ (произносится «си плас плас», допустимо также русскоязычное произношение «си плюс плюс»)

C++ C++ (произносится «си плас плас», допустимо также русскоязычное произношение «си плюс
— компилируемый статически типизированный язык программирования общего назначения. Поддерживая разные парадигмы программирования, сочетает свойства как высокоуровневых, так и низкоуровневых языков. В сравнении с его предшественником — языком C, — наибольшее внимание уделено поддержке объектно-ориентированного и обобщённого программирования. Название «C++» происходит от названия языка C, в котором унарный оператор ++ обозначает инкремент переменной.
Являясь одним из самых популярных языков программирования, C++ широко используется для разработки программного обеспечения. Область его применения включает создание операционных систем, разнообразных прикладных программ, драйверов устройств, приложений для встраиваемых систем, высокопроизводительных серверов, а также развлекательных приложений (например, видеоигры). Существует несколько реализаций языка C++ — как бесплатных, так и коммерческих. Их производят Проект GNU, Microsoft, Intel и Embarcadero (Borland). C++ оказал огромное влияние на другие языки программирования, в первую очередь на Java и C#.
При создании C++ Бьёрн Страуструп стремился сохранить совместимость с языком C. Множество программ, которые могут одинаково успешно транслироваться как компиляторами C, так и компиляторами C++, довольно велико — отчасти благодаря тому, что синтаксис C++ был основан на синтаксисе C.

Слайд 11

Ключевые слова В С89 есть 32 ключевых слова:

Ключевые слова В С89 есть 32 ключевых слова:

Слайд 12

Приоритет операций

Приоритет операций

Слайд 13

Приоритет операций

Приоритет операций

Слайд 14

Базовые типы данных языка С

Базовые типы данных языка С

Слайд 16

Hello в стиле СИ

//*********prog1.cpp*********
#include
void main(void)
{
printf("Hello\n");
}

Hello в стиле СИ //*********prog1.cpp********* #include void main(void) { printf("Hello\n"); }

Слайд 17

Hello в стиле С++

//*********prog2.cpp*********
#include
void main(void)
{
cout<<"Hello"<}

Hello в стиле С++ //*********prog2.cpp********* #include void main(void) { cout }

Слайд 18

Hello в стиле С++ на современных компиляторах

//*********prog2.cpp*********
#include
using namespace std;
int main(void)
{
cout<<"Hello"<return 0;
}

Hello в стиле С++ на современных компиляторах //*********prog2.cpp********* #include using namespace std;

Слайд 19

Использование переменных

Любая переменная, используемая в программе, должна быть описана перед первым её

Использование переменных Любая переменная, используемая в программе, должна быть описана перед первым
использованием. Описать переменную значит указать её имя и тип.
//*********prog3.cpp*********
#include
void main(void)
{
float a,b,c; //Описаны 3 вещественных переменных
a=10; b=5;
c=a/b;
printf(“a=%6.3f\n b=%f\n c=%f\n",a,b,c);
}

Слайд 20

Некоторые функции стандартного ввода-вывода

Функции стандартного ввода - вывода описаны в файле stdio.h.
printf()

Некоторые функции стандартного ввода-вывода Функции стандартного ввода - вывода описаны в файле
- форматный вывод на экран:
int printf(char *format, <список вывода>);
Первый параметр является символьной строкой, которая выводится в поток вывода (экран). В ней могут встречаться спецификаторы формата. Остальные параметры - перечисление переменных и выражений, значения которых выводятся. Каждая спецификация формата имеет вид (параметры в квадратных скобках необязательны):
%[flags][width][.prec]type
Как только в строке встречается спецификатор формата, он замещается значением очередной переменной из списка.

Слайд 21

%[flags][width][.prec]type

%[flags][width][.prec]type

Слайд 22

scanf() - форматный ввод с клавиатуры:
int scanf(char *format, <список ввода>); Первый

scanf() - форматный ввод с клавиатуры: int scanf(char *format, ); Первый параметр
параметр является символьной строкой, которая задает спецификации формата (см. функцию printf()). Остальные параметры - перечисление адресов переменных, в которые вводятся данные. В этом списке перед именами всех переменных, кроме тех, которые вводятся по спецификации типа %s, должен стоять символ &.

Слайд 23

//*********prog4.cpp*********
#include
void main(void)
{
float a,b,c;
printf(“input a:”);
scanf(“%f”,&a);
printf(“input b:”);
scanf(“%f”,&b);
c=a/b;
printf("c=%f\n",c);
}

//*********prog4.cpp********* #include void main(void) { float a,b,c; printf(“input a:”); scanf(“%f”,&a); printf(“input b:”); scanf(“%f”,&b); c=a/b; printf("c=%f\n",c); }

Слайд 24

Вывод значений нескольких переменных

//*********prog4.cpp*********
#include
void main(void)
{
float a=1.5;
int b=7;
char c=‘A’;
char str[]=“Stroka”;
printf(“a=%f b=%d c=%c str=%s\n",a,b,c,str);
}

Вывод значений нескольких переменных //*********prog4.cpp********* #include void main(void) { float a=1.5; int

На экране увидим
a=1.5 b=7 c=A str=Stroka

Слайд 25

Ввод вывод в С++

//*********prog5.cpp*********
#include
void main(void)
{
float a,b,c;
cout<<“input a”;
cin>>a;
cout<<“input b”;
cin>>b;
c=a/b;
cout<<”c=”<}

Ввод вывод в С++ //*********prog5.cpp********* #include void main(void) { float a,b,c; cout

Слайд 26

Целочисленное деление (оба операнда — целые числа)

Деление не целочисленное (операнд 5. -

Целочисленное деление (оба операнда — целые числа) Деление не целочисленное (операнд 5. - вещественное число)
вещественное число)

Слайд 27

Условный оператор if

Усл верно

Опер 2

Опер 1

Опер 3

Да

Нет

if(условие) Опер1;
else Опер 2;
Опер 3;

Полная форма

Условный оператор if Усл верно Опер 2 Опер 1 Опер 3 Да

Слайд 28

Условный оператор if

Усл верно

Опер 1

Опер 2

Да

Нет

if(условие)Опер1;
Опер 2;

Краткая форма

Условный оператор if Усл верно Опер 1 Опер 2 Да Нет if(условие)Опер1; Опер 2; Краткая форма

Слайд 29

Логические операции
Язык С имеет ровно три логические операции: это
&& или (AND);
||

Логические операции Язык С имеет ровно три логические операции: это && или
или (OR);
! или (NOT).
Как принято еще называть логические операции?
Операция "&&" или операция "AND" называется еще операцией "и" или логическим умножением.
Операция "||" или операция "OR" называется еще операцией "или" или логическим сложением.
Операция "! или операция "NOT" называется еще операцией "не" или логическим отрицанием.

Слайд 30

Таблицы истинности логических операций

Операция "&&" называется логическим умножением потому, что выполняется таблица

Таблицы истинности логических операций Операция "&&" называется логическим умножением потому, что выполняется
истинности этой операции, очень напоминающая таблицу обыкновенного умножения из арифметики.
Логическое умножение это такая операция, которая истинна тогда и только тогда, когда истинны оба входящих в нее высказывания.
1 && 1 = 1
0 && 1 = 0
1 && 0 = 0
0 && 0 = 0

Слайд 31

Операция "||" (ИЛИ) называется логическим сложением потому, что выполняется таблица истинности этой

Операция "||" (ИЛИ) называется логическим сложением потому, что выполняется таблица истинности этой
операции, очень напоминающая таблицу обыкновенного сложения из арифметики.
Логическое сложение это такая операция, которая истинна тогда и только тогда, когда истинно хотя бы одно из входящих в нее высказываний.
1 || 1 = 1
0 || 1 = 1
1 || 0 = 1
0 || 0 = 0
Операция "!" ( НЕ) называется логическим отрицанием потому, что выполняется следующая таблица истинности.
Логическое отрицание это такая операция, которая истинна тогда и только тогда, когда ложно входящее в нее высказывание и наоборот.
!1 = 0
!0 = 1

Слайд 32

Пример с полной формой if

/* Объявления переменных x и y и ввод

Пример с полной формой if /* Объявления переменных x и y и
исходных данных */
if( x > 3)
y = 27 + pow(x -3, 3);
else if( x > 1)
y = pow(x, 3);
else if( x > 0)
y = x;
else
y = pow(sin(x), 2) / 2;
/* Вывод значения переменной “y” */

Слайд 33

Пример с краткой формой if

/* Объявления переменных “x” и “y” и ввод

Пример с краткой формой if /* Объявления переменных “x” и “y” и
исходных данных */
if(x > 3) y = 27 + pow(x –3, 3);
if(x <= 3 && x > 1) y = pow(x, 3);
if(x <= 1 && x > 0) y = x;
if(x >= 0) y = pow(sin(x), 2) / 2 ;
/* Вывод значения переменной “y” */

Слайд 34

Операции инкремента и декремента
Операции инкрементации и декрементации являются унарными операциями, то есть

Операции инкремента и декремента Операции инкрементации и декрементации являются унарными операциями, то
операциями, имеющими один операнд.
операнд++ //Постфиксная
++операнд //Префиксная
Операция инкрементации ++ добавляет к операнду единицу.
операнд-- //Постфиксная
--операнд //Префиксная
Операция декрементации -- вычитает из операнда единицу.

Слайд 35

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

Операндом может быть именующее выражение, например, имя переменной. Следующие три строки увеличивают
x на 1:
x = x + 1;
++x;
x++;

Слайд 36

Префиксная (++x, --x ) и постфиксная (x++ , x--) форма
Операции инкрементации и

Префиксная (++x, --x ) и постфиксная (x++ , x--) форма Операции инкрементации
декрементации имеют
префиксную (++x, --x ) и
постфиксную (x++ , x--)
форму записи.
При использовании префиксной формы записи операнд увеличивается или уменьшается сразу же.
Пример 1
x = 3;
y = ++x;
Переменная х сразу же увеличивается до 4 и это значение присваивается переменной y.
При использовании постфиксной формы записи операнд увеличивается или уменьшается после того, как он используется.
Пример 2
x = 3;
y = x++;
Переменной y присваивается значение 3, а затем переменная х увеличивается до 4.

Слайд 37

Сложное присваивание

Сложное Аналог
присваивание
y+=5; y=y+5;
y-=5; y=y-5;
y*=5; y=y*5;
y/=5; y=y/5;

Сложное присваивание Сложное Аналог присваивание y+=5; y=y+5; y-=5; y=y-5; y*=5; y=y*5; y/=5; y=y/5;

Слайд 40

Заполнение лидирующими нулями

Заполнение лидирующими нулями

Слайд 41

Операторы циклов

for
while
do …..while

Операторы циклов for while do …..while

Слайд 42

Оператор for

выр2 верно

Опер 1

Опер 2

Да

Нет

for(выр1; выр2;выр3)
Опер1;
Опер 2;

выр3

выр1

выр1-иницилизационная часть
выр2-проверочная
выр3-послецикловая

Оператор for выр2 верно Опер 1 Опер 2 Да Нет for(выр1; выр2;выр3)

Слайд 43

Пример

int i;
for( i=1;i<=5; i++)
cout< На экране увидим: 12345
Переменную i обычно

Пример int i; for( i=1;i cout На экране увидим: 12345 Переменную i
называют счетчиком цикла;
cout<

Слайд 45

Оператор while

усл верно

Опер

Да

Нет

while(условие)
Опер;

Цикл с предусловием

Оператор while усл верно Опер Да Нет while(условие) Опер; Цикл с предусловием

Слайд 46

Пример

int i;
i=1;
while( i<=5)
{
cout< i++;
}
На экране увидим: 12345

Пример int i; i=1; while( i { cout i++; } На экране увидим: 12345

Слайд 47

Оператор do while

усл верно

Опер

Да

Нет

do
{
Опер;
}
while(условие);

Цикл с постусловием
Тело цикла обязательно
выполнится хоть 1 раз

Оператор do while усл верно Опер Да Нет do { Опер; }

Слайд 48

Пример

int i;
i=1;
do
{
cout< i++;
} while( i<=5);
На экране увидим: 12345

Пример int i; i=1; do { cout i++; } while( i На экране увидим: 12345

Слайд 49

Сравнение операторов циклов

Сравнение операторов циклов

Слайд 50

Задача табулирования

Задача табулирования

Слайд 51

Задача табулирования
//********* for ( format output) ****************
#include
#include
void main(viod)
{
float x,y,xn,xk,dx; int n;

Задача табулирования //********* for ( format output) **************** #include #include void main(viod)
cout<<"xn= "; cin>>xn;
cout<<"xk= "; cin>>xk;
cout<<"n= "; cin>>n;
dx=(xk-xn)/(n-1); x=xn;
cout< <<"x"< for( int i=0;i { y=x*x;
cout< < }
}

Слайд 52

Операторы
break
continue

Операторы break continue

Слайд 53

Операторы break и continue

Часто при возникновении некоторого события удобно иметь возможность досрочно

Операторы break и continue Часто при возникновении некоторого события удобно иметь возможность
завершить цикл. Используемый для этой цели оператор break (разрыв) вызывает немедленный выход из циклов, организуемых с помощью операторов for, while, do-while, а также прекращение оператора switch.

Слайд 54

#include
int main(void)
{
int i;
for(i=1;i<10;i++)
{
if(i==5)
break;
printf(“%d” ,i);
}
return 0;
}
На

#include int main(void) { int i; for(i=1;i { if(i==5) break; printf(“%d” ,i);
экране увидим 1234

Слайд 55

Оператор continue

Оператор continue тоже предназначен для прерывания циклического процесса, организуемого операторами

Оператор continue Оператор continue тоже предназначен для прерывания циклического процесса, организуемого операторами
for, while, do-while. Но в отличии от оператора break, он не прекращает дальнейшее выполнение цикла, а только немедленно переходит к следующей интерации того цикла, в теле которого он оказался. Он как бы имитирует безусловный переход на конечный оператор цикла, но не за ее пределы самого цикла.

Слайд 56

#include
int main(void)
{
int i;
for(i=1;i<10;i++)
{
if(i==5)
continue;
printf(“%d” ,i);
}
return 0;
}
На

#include int main(void) { int i; for(i=1;i { if(i==5) continue; printf(“%d” ,i);
экране увидим 12346789

Слайд 57

Переключатель switch

Оператор switch (переключатель) предназначен для принятия одного из многих решений. Он

Переключатель switch Оператор switch (переключатель) предназначен для принятия одного из многих решений.
выглядит следующим образом:
switch(целое выражение)
{
case константа1: оператор1;
case константа2: оператор2;
...
...
...
case константаn: операторn;
default : оператор;
}

Слайд 58

При выполнении этого оператора вычисляется выражение, стоящее в скобках после ключевого слова

При выполнении этого оператора вычисляется выражение, стоящее в скобках после ключевого слова
switch, которое должно быть целым. Оно, в частности, может быть и символьным значением (в языке Си символьные значения автоматически расширяются до целых значений). Эта целая величина используется в качестве критерия для выбора одного из возможных вариантов. Ее значение сравнивается с константой операторов case. Вместо целой или литерной константы в операторе case может стоять некоторое константное выражение. Значения таких констант (выражений) должны быть различными в разных операторах case. При несовпадении выполняется переход к следующему case и сравнивается его константа. В случае совпадения "константы_i" выполняется "оператор_i", а также все последующие операторы case и default. Если не было ни одного совпадения и имеется оператор default, то выполняется стоящий за ним оператор. Если же оператора default не было, выполнение программы продолжится с оператора, следующего за структурой switch. Таким образом, при каждом выполнении оператора просматриваются все метки case.

Слайд 60

Калькулятор (правильный)

Пример - калькулятор
#include
main()
{
int a,b,c; char op;
printf(

Калькулятор (правильный) Пример - калькулятор #include main() { int a,b,c; char op;
“ Input a op b”):
scanf(“%d “&a); scanf(“%c “&op);
scanf(“%d “&b);
switch(op)
{
case ‘+’:c=a+b; break;
case ‘-’: c=a-b; break;
case ‘*’: c=a*b; break;
case ‘/’: c=a/b; break;
default: printf(“ERROR!!!\n");
}
printf(“%d “,c);
}

Слайд 61

Массивы

Массив - это упорядоченная совокупность данных одного типа. Можно говорить о массивах

Массивы Массив - это упорядоченная совокупность данных одного типа. Можно говорить о
целых чисел, массивов символов и.т.д. Мы можем даже определить массив, элементы которого - массивы( массив массивов), определяя, таким образом, многомерные массивы. Любой массив в программе должен быть описан: после имени массива добавляют квадратные скобки [], внутри которых обычно стоит число, показывающее количество элементов массива. Например, запись int x[10]; определяет x как массив из 10 целых чисел.

Слайд 62

В случае многомерных массивов показывают столько пар скобок , какова размерность массива,

В случае многомерных массивов показывают столько пар скобок , какова размерность массива,
а число внутри скобок показывает размер массива по данному измерению. Например, описание двумерного массива выглядит так: int a[2][5];. Такое описание можно трактовать как матрицу из 2 строк и 5 столбцов. Для обращения к некоторому элементу массива указывают его имя и индекс, заключенный в квадратные скобки(для многомерного массива - несколько индексов , заключенные в отдельные квадратные скобки): a[1][3], x[i] a[0][k+2]. Индексы массива в Си всегда начинаются с 0, а не с 1, т.е. описание int x[5]; порождает элементы x[0], x[1], x[2], x[3], x[4]. Индекс может быть не только целой константой или целой переменной, но и любым выражением целого типа. Переменная с индексами в программе используется наравне с простой переменной (например, в операторе присваивания, в функциях ввода- вывода)..

Слайд 63

Элементам массива могут быть присвоены начальные значения:
int a[6]={5,0,4,-17,49,1};
приведенная запись

Элементам массива могут быть присвоены начальные значения: int a[6]={5,0,4,-17,49,1}; приведенная запись обеспечивает
обеспечивает присвоения a[0]=5; a[1]=0; a[2]=4 ... a[5]=1. Для начального присвоения значений некоторому массиву надо в описании поместить справа от знака = список инициирующих значений, заключенные в фигурные скобки и разделенные запятыми

Слайд 65

Расположение массивов в памяти

double arr[]={0.1,1.1,2.1,3.1,4.1,5.1,6.1,7.1,8.1,9.1};
for(i=0;i<10;i++)
{
cout<<" arr["<

addr=0x1ebd0fa8
arr[1]=1.1 addr=0x1ebd0fb0
arr[2]=2.1 addr=0x1ebd0fb8
arr[3]=3.1 addr=0x1ebd0fc0
arr[4]=4.1 addr=0x1ebd0fc8
arr[5]=5.1 addr=0x1ebd0fd0
arr[6]=6.1 addr=0x1ebd0fd8
arr[7]=7.1 addr=0x1ebd0fe0
arr[8]=8.1 addr=0x1ebd0fe8
arr[9]=9.1 addr=0x1ebd0ff0

Слайд 66

Многомерные массивы
Многомерные массивы - это массивы с более чем одним индексом.
Чаще всего

Многомерные массивы Многомерные массивы - это массивы с более чем одним индексом.
используются двумерные массивы.
При описании многомерного массива
необходимо указать C++,
что массив имеет более чем одно измерение.
int t[3][4];
Описывается двумерный массив, из 3 строк
и 4 столбцов.
Элементы массива:
t[0][0] t[0][1] t[0][2] t[0][3]
t[1][0] t[1][1] t[1][2] t[1][3]
t[2][0] t[2][1] t[2][2] t[2][3]
При выполнении этой команды под массив резервируется место.
Элементы массива располагаются в памяти один за другим.

Слайд 67

В памяти многомерные массивы представляются как одномерный массив, каждый из элементов которого,

В памяти многомерные массивы представляются как одномерный массив, каждый из элементов которого,
в свою очередь, представляет собой массив.
Рассмотрим на примере двумерного массива.
int a[3][2]={4, l, 5,7,2, 9};
Представляется в памяти:
a[0][0] заносится значение 4
a[0][1] заносится значение 1
a[1][0] заносится значение 5
a[1][1] заносится значение 7
a[2][0] заносится значение 2
a[2][1] заносится значение 9
Второй способ инициализации при описании массива
int а[3][2]={ {4,1}, {5, 7}, {2, 9} };
Обращение к элементу массива производится через индексы.
cout<< а[0][0];

Слайд 68

Программа инициализирует массив и выводит его элементы на экран.
#include
int main

Программа инициализирует массив и выводит его элементы на экран. #include int main
()
{
int a[3] [2]={ {1,2}, {3,4}, {5,6} };
int i,j;
for (i=0; i<3; i++)
for(j=0;j<2;j++)
cout <<"\n a["<< i <<"," << j <<"] ="<< a[i][j];
return 0;
}

Слайд 69

//Ввод массива
int a[3] [2];
int i,j;
for (i=0; i<3; i++)
for(j=0;j<2;j++)
{
cout <<"a["<< i

//Ввод массива int a[3] [2]; int i,j; for (i=0; i for(j=0;j { cout cin>> a[i][j]; }
<<"][" << j <<"] =";
cin>> a[i][j];
}

Слайд 70

//обработка массива ( сумма элем.)
int s=0;
for (i=0; i<3; i++)
for(j=0;j<2;j++)
s+=a[i][j];

//обработка массива ( сумма элем.) int s=0; for (i=0; i for(j=0;j s+=a[i][j];

Слайд 71

//вывод на экран
for (i=0; i<3; i++)
{
for(j=0;j<2;j++)
cout < cout< }

//вывод на экран for (i=0; i { for(j=0;j cout cout }

Слайд 73

Указатели

Указатели — это переменные, которые хранят адрес объекта (переменной) в памяти.
Для

Указатели Указатели — это переменные, которые хранят адрес объекта (переменной) в памяти.
объявления указателя нужно добавить звездочку перед именем переменной. Так, например, следующий код создает два указателя, которые указывают на целое число.
int *pNumberOne;
int *pNumberTwo;
Обратили внимание на префикс "p" в обоих именах переменных? Это принятый способ обозначить, что переменная является указателем. Так называемая венгерская нотация.

Слайд 74

Теперь сделаем так, чтобы указатели на что-нибудь указывали:
int some_number=5, some_other_number=10;
pNumberOne = &some_number;
pNumberTwo

Теперь сделаем так, чтобы указатели на что-нибудь указывали: int some_number=5, some_other_number=10; pNumberOne
= &some_other_number;
Знак & (амперсанд) следует читать как "адрес переменной ..." и означает адрес переменной в памяти, который будет возвращен вместо значения самой переменной. Итак, в этом примере pNumberOne установлен и содержит адрес переменной some_number (указывает на some_number).
Если мы хотим получить адрес переменной some_number, мы можем использовать pNumberOne. Если мы хотим получить значение переменной some_number через pNumberOne, нужно добавить звездочку (*) перед pNumberOne (*pNumberOne). Звездочка (*) разыменовывает (превращает в саму переменную) указатель.
cout<< pNumberOne; //Увидим адрес
cout<< *pNumberOne; //Увидим значение

Слайд 76

#include
void main()
{
// объявляем переменные:
int nNumber;
int *pPointer;
// инициализируем объявленные переменные:
nNumber = 15;
pPointer

#include void main() { // объявляем переменные: int nNumber; int *pPointer; //
= &nNumber;
// выводим значение переменной nNumber:
printf("nNumber is equal to : %d\n", nNumber);
// теперь изменяем nNumber через pPointer:
*pPointer = 25;
// убедимся что nNumber изменил свое значение
// в результате предыдущего действия,
// выведя значение переменной ещё раз
printf("nNumber is equal to : %d\n", nNumber);
}

Слайд 77

Динамическая память

Динамическая память позволяет выделять/освобождать память во время работы программы. Код ниже

Динамическая память Динамическая память позволяет выделять/освобождать память во время работы программы. Код
демонстрирует, как выделить память для целого числа:
int *pNumber;
pNumber = new int;
Первая строчка объявляет указатель pNumber. Вторая строчка выделяет память для целого числа (int) и указывает pNumber на эту область памяти. Вот ещё один пример, на этот раз с числом с двойной точностью (double).
double *pDouble;
pDouble = new double;

Слайд 78

Освобождение памяти

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

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

Слайд 80

Операции с указателями

Унарные операции: инкремент и декремент. При выполнении операций ++ и

Операции с указателями Унарные операции: инкремент и декремент. При выполнении операций ++
-- значение указателя увеличивается или уменьшается на длину типа, на который ссылается используемый указатель.
Пример:
int *ptr, a[10]; ptr=&a[5]; ptr++; /* равно адресу элемента a[6] */ ptr--; /* равно адресу элемента a[5] */

Слайд 82

Операции с указателями

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

Операции с указателями В бинарных операциях сложения и вычитания могут участвовать указатель
величина типа int. При этом результатом операции будет указатель на исходный тип, а его значение будет на указанное число элементов больше или меньше исходного.
Пример:
int *ptr1, *ptr2, a[10]; int i=2; ptr1=a+(i+4); /* равно адресу элемента a[6] */ ptr2=ptr1-i; /* равно адресу элемента a[4] */

Слайд 84

Операции с указателями

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

Операции с указателями В операции вычитания могут участвовать два указателя на один
тот же тип. Результат такой операции имеет тип int и равен числу элементов исходного типа между уменьшаемым и вычитаемым, причем если первый адрес младше, то результат имеет отрицательное значение.
Пример:
int *ptr1, *ptr2, a[10]; int i; ptr1=a+4; ptr2=a+9; i=ptr1-ptr2; /* равно -5 */ i=ptr2-ptr1; /* равно 5 */

Слайд 86

Операции с указателями

Значения двух указателей на одинаковые типы можно сравнивать в операциях

Операции с указателями Значения двух указателей на одинаковые типы можно сравнивать в
==, !=, <, <=",">, >= при этом значения указателей рассматриваются просто как целые числа, а результат сравнения равен 0 (ложь) или 1 (истина).
Пример:
int *ptr1, *ptr2, a[10]; ptr1=a+5; ptr2=a+7; if (prt1>ptr2) a[3]=4;
В данном примере значение ptr1 меньше значения ptr2 и поэтому оператор a[3]=4 не будет выполнен.

Слайд 87

Методы доступа к элементам массивов

Для доступа к элементам массива существует два различных

Методы доступа к элементам массивов Для доступа к элементам массива существует два
способа. Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например, array[16]=3 или array[i+2]=7. При таком способе доступа записываются два выражения, причем второе выражение заключается в квадратные скобки. Одно из этих выражений должно быть указателем, а второе - выражением целого типа.

Слайд 88

Методы доступа к элементам массивов

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

Методы доступа к элементам массивов Второй способ доступа к элементам массива связан
использованием адресных выражений и операции разадресации в форме *(array+16)=3 или *(array+i+2)=7.
При реализации на компьютере первый способ приводится ко второму, т.е. индексное выражение преобразуется к адресному. Для приведенного примера array[16] преобразуются в *(array+16).

Слайд 89

Функции

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

Функции Мощность языка программирования С во многом определяется легкостью и гибкостью в
и использовании функций в программах на языке программирования С. В отличие от других языков программирования высокого уровня в языке программирования С нет деления на процедуры, подпрограммы и функции, здесь вся программа строится только из функций.
Функция - это совокупность объявлений и операторов, обычно предназначенная для решения определенной задачи. Каждая функция должна иметь имя, которое используется для ее объявления, определения и вызова. В любой программе на С должна быть функция с именем main (главная функция), именно с этой функции, в каком бы месте программы она не находилась, начинается выполнение программы.

Слайд 90

функции

При вызове функции ей при помощи аргументов (формальных параметров) могут быть переданы

функции При вызове функции ей при помощи аргументов (формальных параметров) могут быть
некоторые значения (фактические параметры), используемые во время выполнения функции. Функция может возвращать некоторое (одно !) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился. Допускается также использовать функции не имеющие аргументов и функции не возвращающие никаких значений. Действие таких функций может состоять, например, в изменении значений некоторых переменных, выводе на печать некоторых текстов и т.п..

Слайд 91

Функции

С использованием функций в языке СИ связаны три понятия:
определение функции (описание действий,

Функции С использованием функций в языке СИ связаны три понятия: определение функции
выполняемых функцией)
объявление функции (задание формы обращения к функции)
вызов функции.

Слайд 92

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

Определение функции задает тип возвращаемого значения, имя функции, типы и число формальных
параметров, а также объявления переменных и операторы, называемые телом функции, и определяющие действие функции. Пример:
int max ( int a, int b)
{ if (a>b)
return a;
else
return b;
}
В данном примере определена функция с именем max, имеющая 2 параметра. Функция возвращает целое значение ( максимальное из а и b.

Слайд 93

В языке СИ нет требования, чтобы определение функции обязательно предшествовало ее вызову.

В языке СИ нет требования, чтобы определение функции обязательно предшествовало ее вызову.
Определения используемых функций могут следовать за определением функции main, перед ним, или находится в другом файле.
Чтобы компилятор мог осуществить проверку соответствия типов передаваемых фактических параметров типам формальных параметров до вызова функции нужно поместить объявление (прототип) функции.
Объявление функции имеет такой же вид, что и определение функции, с той лишь разницей, что тело функции отсутствует, и имена формальных параметров тоже могут быть опущены. Для функции, определенной в последнем примере, прототип может иметь вид
int max (int a, int b);

Слайд 94

Функции

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

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

Слайд 95

В соответствии с синтаксисом языка СИ определение функции имеет следующую форму:

В соответствии с синтаксисом языка СИ определение функции имеет следующую форму: [спецификатор-класса-памяти]
[спецификатор-класса-памяти] [спецификатор-типа] имя-функции
([список-формальных-параметров])
{ тело-функции }
Необязательный спецификатор-класса-памяти задает класс памяти функции, который может быть static или extern.

Слайд 96

Функции ( возвращаемое значение)

Функция возвращает значение если ее выполнение заканчивается оператором return,

Функции ( возвращаемое значение) Функция возвращает значение если ее выполнение заканчивается оператором
содержащим некоторое выражение. Указанное выражение вычисляется, преобразуется, если необходимо, к типу возвращаемого значения и возвращается в точку вызова функции в качестве результата. Если оператор return не содержит выражения или выполнение функции завершается после выполнения последнего ее оператора (без выполнения оператора return), то возвращаемое значение не определено. Для функций, не использующих возвращаемое значение, должен быть использован тип void, указывающий на отсутствие возвращаемого значения. Если функция определена как функция, возвращающая некоторое значение, а в операторе return при выходе из нее отсутствует выражение, то поведение вызывающей функции после передачи ей управления может быть непредсказуемым.

Слайд 97

Список-формальных-параметров

Список-формальных-параметров - это последовательность объявлений формальных параметров, разделенная запятыми. Формальные параметры -

Список-формальных-параметров Список-формальных-параметров - это последовательность объявлений формальных параметров, разделенная запятыми. Формальные параметры
это переменные, используемые внутри тела функции и получающие значение при вызове функции путем копирования в них значений соответствующих фактических параметров. Список-формальных-параметров может заканчиваться запятой (,) или запятой с многоточием (,...), это означает, что число аргументов функции переменно. Однако предполагается, что функция имеет, по крайней мере, столько обязательных аргументов, сколько формальных параметров задано перед последней запятой в списке параметров. Такой функции может быть передано большее число аргументов, но над дополнительными аргументами не проводится контроль типов.
Если функция не использует параметров, то наличие круглых скобок обязательно, а вместо списка параметров рекомендуется указать слово void.

Слайд 98

Формальные параметры

Порядок и типы формальных параметров должны быть одинаковыми в определении функции

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

Слайд 99

Передача параметров по значению

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

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

Слайд 100

Пример:
/* Неправильное использование параметров */
void change (int x, int

Пример: /* Неправильное использование параметров */ void change (int x, int y)
y)
{ int k=x;
x=y;
y=k;
}
В данной функции значения переменных x и y, являющихся формальными параметрами, меняются местами, но поскольку эти переменные существуют только внутри функции change, значения фактических параметров, используемых при вызове функции, останутся неизменными.

Слайд 101

Передача параметров по указателю

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

Передача параметров по указателю Однако, если в качестве параметра передать указатель на
переменную, то используя операцию разадресации можно изменить значение этой переменной.

Слайд 102

/* Правильное использование параметров */
void change (int *x, int *y)

/* Правильное использование параметров */ void change (int *x, int *y) {
{ int k=*x;
*x=*y;
*y=k;
}
При вызове такой функции в качестве фактических параметров должны быть использованы не значения переменных, а их адреса
change (&a,&b);

Слайд 103

Передача параметров по ссылке

/* Правильное использование параметров */
void change (int &x,

Передача параметров по ссылке /* Правильное использование параметров */ void change (int
int &y)
{ int k=x;
x=y;
y=k;
}
Вызов такой функции:
change (a,b);
Фактически передаются адреса!

Слайд 104

Ввод массива

#include
void vvod(float mas[],int n)
{
int i;
for(i=0; i {
printf("mas[%d]=",

Ввод массива #include void vvod(float mas[],int n) { int i; for(i=0; i
i);
scanf("%f", &mas[i]);
}
}

Слайд 105

Вывод массива

void vivod(float mas[], int n)
{
int i;
for(i=0; i printf("mas[%d]=%7.3f\n",i,mas[i]);
}

Вывод массива void vivod(float mas[], int n) { int i; for(i=0; i printf("mas[%d]=%7.3f\n",i,mas[i]); }

Слайд 106

Обработка массива ( функция возвращает сумму отрицательных элементов)

float otr(float mas[],int n)
{
int i;

Обработка массива ( функция возвращает сумму отрицательных элементов) float otr(float mas[],int n)
float s=0;
for(i=0; i if(mas[i]<0)
s+=mas[i]; //s=s+mas[i];
return s;
}

Слайд 107

Вызов функций

int main()
{
float s;
int n;
char c;
float a[10];
printf("vvesti razmer\n");

Вызов функций int main() { float s; int n; char c; float
scanf("%d",&n);
vvod(a,n);
vivod(a,n);
printf("sumotr=%7.3f\n",otr(a,n));
scanf("%c\n",&c);
return 0;
}

Слайд 108

Лаб. Раб. 7 вар 9

Лаб. Раб. 7 вар 9

Слайд 109

Функция main

int main()
{
int n; char c;
float a[100], b[100], c[100], as[100],

Функция main int main() { int n; char c; float a[100], b[100],
bs[100], cs[100], x[100], y[100];
printf("vvesti razmer\n");
scanf("%d",&n);
vvod(a,n); vvod(b,n);vvod(c,n);
strih(a,as,n); strih(b,bs,n); strih(c,cs,n);
calc(as,bs,x,n); calc(bs,cs,y,n);
vivod(x,n); vivod(y,n);
scanf("%c\n",&c);
return 0;
}

Слайд 110

Функция, возвращающая сумму элементов массива

float sum(float mas[],int n)
{
int i;
float s=0;

Функция, возвращающая сумму элементов массива float sum(float mas[],int n) { int i;
for(i=0; i s+=mas[i];
return s;
}

Слайд 111

Функция strih

void strih(float m[],float ms[],int n)
{
int i;
float s; s=sum(m,n);
for(i=0;

Функция strih void strih(float m[],float ms[],int n) { int i; float s;
i ms[i]=m[i]/s;
}

Слайд 112

Функция calc

void calc(float m1[],float m2[],float mrez[],int n)
{
int i;
for(i=0; i

Функция calc void calc(float m1[],float m2[],float mrez[],int n) { int i; for(i=0; i mrez[i]=m1[i]+m2[i]; }
mrez[i]=m1[i]+m2[i];
}

Слайд 113

Прототипы функций

void vvod(float mas[],int n);
void vivod(float mas[], int n);
float sum(float mas[],int n);
void

Прототипы функций void vvod(float mas[],int n); void vivod(float mas[], int n); float
strih(float m[],float ms[],int n);
void calc(float m1[],float m2[],float mrez[],int n);
Прототипы функций указываются в том случае, если функция не определена до первого её вызова!

Слайд 118

Проекты

Проекты

Слайд 123

Препроцессор Компилятор Компоновщик
func.cpp func.o
main.cpp main.o mas2fun_pro.exe
библиотеки

Препроцессор Компилятор Компоновщик func.cpp func.o main.cpp main.o mas2fun_pro.exe библиотеки

Слайд 124

Область действия ( видимость ) переменных

#include
void main(void)
{ int a=10;
{
int a=5;
cout< }
cout<}

Переменная видна в

Область действия ( видимость ) переменных #include void main(void) { int a=10;
том блоке программы, в котором она определена, и во вложенных блоках. Локальное имя преобладает над глобальным.

Слайд 125

Автоматические и статические преременные

#include
int calc()
{
int a=0;
a++;
return(a);
}
void main(void)
{
int x;
x=calc();

Автоматические и статические преременные #include int calc() { int a=0; a++; return(a);
cout<<"x="< x=calc();
cout<<"x="< cin>>x;
}

Автоматическая переменная создается каждый раз при вызове
функции, а статическая один раз.
На экране увидим
Х=1
Х=1

Слайд 126

#include
int calc()
{
static int a=0;
a++;
return(a);
}
void main(void)
{
int x;
x=calc();
cout<<"x="<

#include int calc() { static int a=0; a++; return(a); } void main(void)
x=calc();
cout<<"x="< cin>>x;
}

Автоматическая переменная создается каждый раз при вызове
функции, а статическая один раз.
На экране увидим
Х=1
Х=2

Слайд 127

Динамические массивы

#include
void inputarr(int *inarr, int n, char arrname[])
{
int i;
cout << "Input

Динамические массивы #include void inputarr(int *inarr, int n, char arrname[]) { int
the " << n << " digits for array " << arrname << ":\n";
for (i=0; i> *(inarr+i);
}

Слайд 128

void outputarr(int *outarr, int n, char arrname[])
{
int i;
for (i=0; i cout

void outputarr(int *outarr, int n, char arrname[]) { int i; for (i=0; i cout }
<< arrname << "[" << i << "]=" << *(outarr+i) << "\n";
}

Слайд 129

void createoutarr(int arr1[], int arr2[], int outarr[], int n)
{
int i;
for (i=0; i

void createoutarr(int arr1[], int arr2[], int outarr[], int n) { int i; for (i=0; i }
i++) outarr[i] = arr1[i] - arr2[i];
}

Слайд 130

void main()
{
int *x,*y,*z,*xy,*xz,*yz;
int Size;
cout<<"Enter size of array ";
cin>>Size;
x =new int[Size];
y

void main() { int *x,*y,*z,*xy,*xz,*yz; int Size; cout cin>>Size; x =new int[Size];
=new int[Size];
z =new int[Size];
xy=new int[Size];
xz=new int[Size];
yz=new int[Size];

Слайд 131

inputarr(x, Size, "x");
inputarr(y, Size, "y");
inputarr(z, Size, "z");
createoutarr(x, y, xy, Size);
createoutarr(x, z, xz,

inputarr(x, Size, "x"); inputarr(y, Size, "y"); inputarr(z, Size, "z"); createoutarr(x, y, xy,
Size);
createoutarr(y, z, yz, Size);
outputarr(xy, Size, "xy");
outputarr(xz, Size, "xz");
outputarr(yz, Size, "yz");

Слайд 132

Освобождение динамической памяти

delete [] x;
delete [] y;
delete [] z;
delete [] xy;
delete

Освобождение динамической памяти delete [] x; delete [] y; delete [] z;
[] xz;
delete [] yz;
}

Слайд 133

Передача имен функций в качестве параметров

Функцию можно вызвать через указатель на нее.

Передача имен функций в качестве параметров Функцию можно вызвать через указатель на
Для этого объявляется указатель соответствующего типа и ему с помощью операции взятия адреса присваивается адрес функции:
void f(int a ){ /* ... */ }//определение функции
void (*pf)(int);//указатель на функцию
...
pf = &f;/* указателю присваивается адрес функции (можно написать pf = f;) */
pf(10);/* функция f вызывается через указатель pf (можно написать (*pf)(10) ) */

Слайд 134

#include
#include
#include
int f(int a ){ return a; }
int (*pf)(int);
int main(void)
{
pf

#include #include #include int f(int a ){ return a; } int (*pf)(int);
= &f;
printf("%d\n",pf(10));
pf=f;
printf("%d\n",pf(10));
return 0;
}

На экране
10
10

Слайд 135

#include
#include
#include
int f(int a ){ return a; }
int (*pf)(int);
void fun(int

#include #include #include int f(int a ){ return a; } int (*pf)(int);
(*pf)(int) , int x)
{
printf("%d\n",pf(x));
}
int main(void)
{
pf = &f;
printf("%d\n",pf(10));
pf=f;
printf("%d\n",pf(10));
fun(f,20);
return 0;
}

На экране
10
10
20

Слайд 136

Перегрузка функций

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

Перегрузка функций Перегрузка функций — это механизм, который позволяет двум родственным функциям
C++ несколько функций могут иметь одинаковые имена, но при условии, что их параметры будут различными. Такую ситуацию называют перегрузкой функций (function overloading), а функции, которые в ней задействованы, —перегруженными (overloaded). Перегрузка функций — один из способов реализации полиморфизма в C++.
Рассмотрим простой пример перегрузки функций.

Слайд 138

Аргументы, передаваемые функции по умолчанию

В C++ мы можем придать параметру некоторое значение,

Аргументы, передаваемые функции по умолчанию В C++ мы можем придать параметру некоторое
которое будет автоматически использовано, если при вызове функции не задается аргумент, соответствующий этому параметру. Аргументы, передаваемые функции по умолчанию, можно использовать, чтобы упростить обращение к сложным функциям, а также в качестве "сокращенной формы" перегрузки функций.
Задание аргументов, передаваемых функции по умолчанию, синтаксически аналогично инициализации переменных. Рассмотрим следующий пример, в котором объявляется функция myfunc(), принимающая один аргумент типа double с действующим по умолчанию значением 0.0 и один символьный аргумент с действующим по умолчанию значением 'Х'.
void myfunc(double num = 0.0, char ch = 'Х')
{
.
.
}
После такого объявления функцию myfunc() можно вызвать одним из трех следующих способов.
myfunc(198.234, 'A'); // Передаем явно заданные значения.
myfunc(10.1); // Передаем для параметра num значение 10.1, а для параметра ch позволяем применить аргумент, задаваемый по умолчанию ('Х').
myfunc(); // Для обоих параметров num и ch позволяем применить аргументы, задаваемые по умолчанию.
При первом вызове параметру num передается значение 198.234, а параметру ch— символ'А'. Во время второго вызова параметру num передается значение10.1, а параметр ch по умолчанию устанавливается равным символу‘ Х'. Наконец, в результате третьего вызова как параметр num, так и параметр ch по умолчанию устанавливаются равными значениям, заданным в объявлении функции.

Слайд 139

Включение в C++ возможности передачи аргументов по умолчанию позволяет программистам упрощать код

Включение в C++ возможности передачи аргументов по умолчанию позволяет программистам упрощать код
программ. Чтобы предусмотреть максимально возможное количество ситуаций и обеспечить их корректную обработку, функции часто объявляются с большим числом параметров, чем необходимо в наиболее распространенных случаях. Поэтому благодаря применению аргументов по умолчанию программисту нужно указывать не все аргументы (используемые в общем случае), а только те, которые имеют смысл для определенной ситуации.
Аргумент, передаваемый функции по умолчанию, представляет собой значение, которое будет автоматически передано параметру функции в случае, если аргумент, соответствующий этому параметру, явным образом не задан.
Насколько полезна возможность передачи аргументов по умолчанию, показано на примере функции clrscr(), представленной в следующей программе. Функция clrscr() очищает экран путем вывода последовательности символов новой строки (это не самый эффективный способ, но он очень подходит для данного примера). Поскольку в наиболее часто используемом режиме представления видеоизображений на экран дисплея выводится25 строк текста, то в качестве аргумента по умолчанию используется значение 25. Но так как в других видеорежимах на экране может отображаться больше или меньше 25 строк, аргумент, действующий по умолчанию, можно переопределить, явно указав нужное значение.

Слайд 141

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

Важно понимать, что все параметры, которые принимают значения по умолчанию, должны быть
расположены справа от остальных. Например, следующий прототип функции содержит ошибку.
// Неверно!
void f(int а = 1, int b);
Если вы начали определять параметры, которые принимают значения по умолчанию, нельзя после них указывать параметры, задаваемые при вызове функции только явным образом. Поэтому следующее объявление также неверно и не будет скомпилировано.
int myfunc(float f, char *str, int i=10, int j);
Поскольку для параметра i определено значение по умолчанию, для параметра j также нужно задать значение по умолчанию.

Слайд 142

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

Несмотря на то что аргументы, передаваемые функции

Об использовании аргументов, передаваемых по умолчанию Несмотря на то что аргументы, передаваемые
по умолчанию, — очень мощное средство программирования (при их корректном использовании), с ними могут иногда возникать проблемы. Их назначение — позволить функции эффективно выполнять свою работу, обеспечивая при всей простоте этого механизма значительную гибкость. В этом смысле все передаваемые по умолчанию аргументы должны отражать способ наиболее общего использования функции или альтернативного ее применения. Если не существует некоторого единого значения, которое обычно присваивается тому или иному параметру, то и нет смысла объявлять соответствующий аргумент по умолчанию. На самом деле объявление аргументов, передаваемых функции по умолчанию, при недостаточном для этого основании деструктуризирует код, поскольку такие аргументы способны сбить с толку любого, кому придется разбираться в такой программе. Наконец, основным принципом использования аргументов по умолчанию должен быть, как у врачей, принцип "не навредить". Другими словами, случайное использование аргумента по умолчанию не должно привести к необратимым отрицательным последствиям. Ведь такой аргумент можно просто забыть указать при вызове некоторой функции, и, если это случится, подобный промах не должен вызвать, например, потерю важных данных!

Слайд 144

Перегрузка функций и неоднозначность

Неоднозначность возникает тогда, когда компилятор не может определить различие

Перегрузка функций и неоднозначность Неоднозначность возникает тогда, когда компилятор не может определить
между двумя перегруженными функциями.
Возможны ситуации, в которых компилятор не способен сделать выбор между двумя (или более) корректно перегруженными функциями. Такие ситуации и называют неоднозначными. Инструкции, создающие неоднозначность, являются
ошибочными, а программы, которые их содержат, скомпилированы не будут.
Основной причиной неоднозначности в C++ является автоматическое преобразование типов. В C++ делается попытка автоматически преобразовать тип аргументов, используемых для вызова функции, в тип параметров, определенных функцией. Рассмотрим пример.
int myfunc(double d);
.
.
.
cout << myfunc('c'); // Ошибки нет, выполняется преобразование типов.
Как отмечено в комментарии, ошибки здесь нет, поскольку C++ автоматически преобразует символ 'c' в его double-эквивалент. Вообще говоря, в C++ запрещено довольно мало видов преобразований типов. Несмотря на то что автоматическое преобразование типов — это очень удобно, оно, тем не менее, является главной причиной неоднозначности. Рассмотрим следующую программу.

Слайд 145

// Неоднозначность вследствие перегрузки функций.
#include
using namespace std;
float myfunc(float i);
double myfunc(double i);
int

// Неоднозначность вследствие перегрузки функций. #include using namespace std; float myfunc(float i);
main()
{
// Неоднозначности нет, вызывается функция myfunc(double).
cout << myfunc (10.1) << " ";
// Неоднозначность.
cout << myfunc(10);
return 0;
}
float myfunc(float i)
{
return i;
}
double myfunc(double i)
{
return -i;
}

Слайд 146

Здесь благодаря перегрузке функция myfunc() может принимать аргументы либо типа float, либо типа double.

Здесь благодаря перегрузке функция myfunc() может принимать аргументы либо типа float, либо
При выполнении строки кода
cout << myfunc (10.1) << " ";
не возникает никакой неоднозначности: компилятор "уверенно" обеспечивает вызов функции myfunc(double), поскольку, если не задано явным образом иное, все литералы с плавающей точкой в C++ автоматически получают тип double. Но при вызове функции myfunc() с аргументом, равным целому числу 10, в программу вносится неоднозначность, поскольку компилятору неизвестно, в какой тип ему следует преобразовать этот аргумент: float или double. Оба преобразования допустимы. В такой неоднозначной ситуации будет выдано сообщение об ошибке, и программа не скомпилируется.
На примере этой программы хотелось бы подчеркнуть, что неоднозначность в ней вызвана не перегрузкой функции myfunc(), объявленной дважды для приема double- и float-аргумента, а использованием при конкретном вызове функции myfunc() аргумента неопределенного для преобразования типа. Другими словами, ошибка состоит не в перегрузке функции myfunc(), а в конкретном ее вызове.

Слайд 149

Возврат ссылок

Функция может возвращать ссылку. В программировании на C++ предусмотрено несколько применений

Возврат ссылок Функция может возвращать ссылку. В программировании на C++ предусмотрено несколько
для ссылочных значений, возвращаемых функциями. Если функция возвращает ссылку, это означает, что она возвращает неявный указатель на значение, передаваемое ею в инструкции return. Этот факт открывает поразительные возможности: функцию, оказывается, можно использовать в левой части инструкции присваивания!

Слайд 151

Рассмотрим эту программу подробнее. Судя по прототипу функции f(), она должна возвращать ссылку

Рассмотрим эту программу подробнее. Судя по прототипу функции f(), она должна возвращать
на double-значение. За объявлением функции f() следует объявление глобальной переменной val, которая инициализируется значением 100. При выполнении следующей инструкции выводится исходное значение переменной val.
cout << f() << '\n'; // Отображаем значение val.
После вызова функция f() возвращает ссылку на переменную val. Поскольку функция f() объявлена с "обязательством" вернуть ссылку, при выполнении строки
return val; // Возвращаем ссылку на val.
автоматически возвращается ссылка на глобальную переменную val. Эта ссылка затем используется инструкцией cout для отображения значения val.
При выполнении строки
newval = f(); //Присваиваем значение val переменной newval.
ссылка на переменную val, возвращенная функцией f(), используется для присвоения значения val переменной newval.
А вот самая интересная строка в программе.
f() = 99.1; // Изменяем значение val.
При выполнении этой инструкции присваивания значение переменной val становится равным числу 99,1. И вот почему: поскольку функция f() возвращает ссылку на переменную val, эта ссылка и является приемником инструкции присваивания. Таким образом, значение 99,1 присваивается переменной val косвенно, через ссылку на нее, которую возвращает функция f().
Наконец, при выполнении строки
cout << f() << '\n'; // Отображаем новое значение val.
отображается новое значение переменной val (после того, как ссылка на переменную val будет возвращена в результате вызова функции f() в инструкции cout).

Слайд 152

программа изменяет значения второго и четвертого элементов массива

программа изменяет значения второго и четвертого элементов массива

Слайд 153

Функция change_it() объявлена как возвращающая ссылку на значение типа double. Говоря более конкретно, она

Функция change_it() объявлена как возвращающая ссылку на значение типа double. Говоря более
возвращает ссылку на элемент массива vals, который задан ей в качестве параметра i. Таким образом, при выполнении следующей инструкции функции main()
change_it(1) = 5298.23; // Изменяем 2-йэлемент.
функция change_it() возвращает ссылку на элемент vals[1]. Через эту ссылку элементу vals[1] теперь присваивается значение 5298,23. Аналогичные события происходят при выполнении и этой инструкции.
change_it(3) = -98.8; //Изменяем4-йэлемент.
Поскольку функция change_it() возвращает ссылку на конкретный элемент массива vals, ее можно использовать в левой части инструкции для присвоения нового значения соответствующему элементу массива.
Организуя возврат функцией ссылки, необходимо позаботиться о том, чтобы объект, на который она ссылается, не выходил за пределы действующей области видимости. Например:
//Здесь ошибка: нельзя возвращать ссылку
//на локальную переменную.
int &f()
{
int i=10;
return i;
}
При завершении функции f() локальная переменная i выйдет за пределы области видимости. Следовательно, ссылка на переменную i, возвращаемая функцией f(), будет неопределенной. В действительности некоторые компиляторы не скомпилируют функцию f() в таком виде, и именно по этой причине. Однако проблема такого рода может быть создана опосредованно, поэтому нужно внимательно отнестись к тому, на какой объект будет возвращать ссылку ваша функция.

Слайд 154

Создание ограниченного массива

Ссылочный тип в качестве типа значения, возвращаемого функцией, можно с

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

Слайд 156

Независимые ссылки

Понятие ссылки включено в C++ главным образом для поддержки способа передачи

Независимые ссылки Понятие ссылки включено в C++ главным образом для поддержки способа
параметров "по ссылке" и для использования в качестве ссылочного типа значения, возвращаемого функцией. Несмотря на это, можно объявить независимую переменную ссылочного типа, которая и называется независимой ссылкой. Однако, справедливости ради, необходимо сказать, что эти независимые ссылочные переменные используются довольно редко, поскольку они могут "сбить с пути истинного" вашу программу. Сделав (для очистки совести) эти замечания, мы все же можем уделить независимым ссылкам некоторое внимание.
Независимая ссылка — это просто еще одно название для переменных некоторого иного типа.
Независимая ссылка должна указывать на некоторый объект. Следовательно, независимая ссылка должна быть инициализирована при ее объявлении. В общем случае это означает, что ей будет присвоен адрес некоторой ранее объявленной переменной. После этого имя такой ссылочной переменной можно применять везде, где может быть использована переменная, на которую она ссылается. И в самом деле, между ссылкой и переменной, на которую она ссылается, практически нет никакой разницы. Рассмотрим, например, следующую программу.

Слайд 158

Функция Си/ C++ - qsort

//void qsort(void *base, size_t nelem,
//size_t width, int (*fcmp)(const

Функция Си/ C++ - qsort //void qsort(void *base, size_t nelem, //size_t width,
void *, const void *));
Описание.
Функция qsort выполняет алгоритм быстрой сортировки, чтобы
отсортировать массив из nelem элементов, каждый элемент размером
width байт. Аргумент base является указателем на базу массива,
который нужно отсортировать. Функция qsort перезаписывает этот
массив с отсортированными элементами.
Аргумент fcmp является указателем на функцию, поставляемую пользователем, которая сравнивает два элемента массива и
возвращает значение, определяющее их отношение.
Функция qsort может вызывать процедуру fcmp один или
несколько раз в процессе сортировки, передавая при каждом вызове
указатели на два элемента массива. Процедура должна сравнивать
элементы, а затем возвращать одно из следующих значений:
Значение Его смысл
меньше 0 element 1 меньше element 2
0 element 1 равен element 2
больше 0 element 1 больше element 2

Слайд 159

#include
#include
#include
int sort_function( const void *a, const void *b);
char list[5][4]

#include #include #include int sort_function( const void *a, const void *b); char
= { "cat", "car", "cab", "cap", "can" };
int main(void)
{
int x;
qsort((void *)list, 5, sizeof(list[0]), sort_function);
for (x = 0; x < 5; x++)
printf("%s\n", list[x]);
return 0;
}
int sort_function( const void *a, const void *b)
{
return( strcmp((char *)a,(char *)b) );
}

Слайд 161

Двумерные динамические массивы

Санкт-Петербургский государственный университет телекоммуникаций им. проф. М.А. Бонч-Бруевича

Двумерные динамические массивы Санкт-Петербургский государственный университет телекоммуникаций им. проф. М.А. Бонч-Бруевича

Слайд 162

Вспомним одномерные динамич. массивы

Вспомним одномерные динамич. массивы

Слайд 163

Работаем с дин. масс. как с обычным массивом

Работаем с дин. масс. как с обычным массивом

Слайд 164

Освобождаем память

Освобождаем память

Слайд 165

Двумерный динамический массив

Двумерный динамический массив

Слайд 166

Недостаток!

Недостаток!

Слайд 167

Вывод! Нужно сделать массив указателей динамическим!

Вывод! Нужно сделать массив указателей динамическим!

Слайд 172

Массив указателей на функции Массив указателей на функции определяется точно также, как и

Массив указателей на функции Массив указателей на функции определяется точно также, как
обычный массив – с помощью квадратных скобок после имени: float (*menu[4])(float, float);

Слайд 176

Часто указатели на функцию становятся громоздкими. Работу с ними можно упростить, если

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

Слайд 179

Препроцессор C++

Препроцессор C++ подвергает программу различным текстовым преобразованиям до реальной трансляции исходного

Препроцессор C++ Препроцессор C++ подвергает программу различным текстовым преобразованиям до реальной трансляции
кода в объектный. Препроцессор обрабатывает команды, называемые директивами препроцессора
Препроцессор C++ включает следующие директивы.
Все директивы препроцессора начинаются с символа '#‘.

Слайд 180

Директива #define

Директива #define используется для определения идентификатора и символьной последовательности, которая будет подставлена вместо

Директива #define Директива #define используется для определения идентификатора и символьной последовательности, которая
идентификатора везде, где он встречается в исходном коде программы. Этот идентификатор называется макроименем, а процесс замены —макроподстановкой (реализацией макрорасширения). Общий формат использования этой директивы имеет следующий вид.
#define макроимя последовательность_символов
Обратите внимание на то, что здесь нет точки с запятой. Заданная последовательность_символов завершается только символом конца строки. Между элементами макроимя (имя_макроса) и последовательность_символов может быть любое количество пробелов.
Итак, после включения этой директивы каждое вхождение текстового фрагмента, определенное как макроимя, заменяется заданным элементом последовательность_символов. Например, если вы хотите использовать слово UP в качестве значения 1 и слово DOWN в качестве значения 0, объявите такие директивы #define.
#define UP 1
#define DOWN 0
Данные директивы вынудят компилятор подставлять 1 или 0 каждый раз, когда в файле исходного кода встретится слово UP или DOWN соответственно. Например, при выполнении инструкции:
cout << UP << ' ' << DOWN << ' ' << UP + UP;
На экран будет выведено следующее:
1 0 2

Слайд 181

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

После определения имени макроса его можно использовать как часть определения других макроимен.
Например, следующий код определяет имена ONE,TWO и THREE и соответствующие им значения.
#define ONE 1
#define TWO ONE+ONE
#define THREE ONE+TWO
Важно понимать, что макроподстановка — это просто замена идентификатора соответствующей строкой. Следовательно, если вам нужно определить стандартное сообщение, используйте код, подобный этому.
#define GETFILE "Введите имя файла"
// ...
Препроцессор заменит строкой "Введите имя файла" каждое вхождение идентификатора GETFILE. Для компилятора эта cout-инструкция
cout << GETFILE;
в действительности выглядит так.
cout << "Введите имя файла";
Никакой текстовой замены не произойдет, если идентификатор находится в строке, заключенной в кавычки. Например, при выполнении следующего кода
#define GETFILE "Введите имя файла"
// ...
cout << "GETFILE - это макроимя\n";
на экране будет отображена эта информация
GETFILE - это макроимя,
а не эта:
Введите имя файла - это макроимя

Слайд 182

Если текстовая последовательность не помещается на строке, ее можно продолжить на следующей,

Если текстовая последовательность не помещается на строке, ее можно продолжить на следующей,
поставив обратную косую черту в конце строки, как показано в этом примере.
#define LONG_STRING "Это очень длинная последовательность,\
которая используется в качестве примера."
Среди С++ - программистов принято использовать для макроимен прописные буквы. Это соглашение позволяет с первого взгляда понять, что здесь используется макроподстановка. Кроме того, лучше всего поместить все директивы #define в начало файла или включить в отдельный файл, чтобы не искать их потом по всей программе.
Макроподстановки часто используются для определения "магических чисел« программы. Например, у вас есть программа, которая определяет некоторый массив, и ряд функций, которые получают доступ к нему. Вместо "жесткого" кодирования размера массива с помощью константы лучше определить имя, которое бы представляло размер, а затем использовать это имя везде, где должен стоять размер массива. Тогда, если этот размер придется изменить, вам достаточно будет внести только одно изменение, а затем перекомпилировать программу. Рассмотрим пример.
#define MAX_SIZE 100
// ...
float balance[MAX_SIZE];
double index[MAX_SIZE];
int num_emp[MAX_SIZE];

Слайд 183

Макроопределения, действующие как функции

Директива #define имеет еще одно назначение: макроимя может использоваться с аргументами.

Макроопределения, действующие как функции Директива #define имеет еще одно назначение: макроимя может
При каждом вхождении макроимени связанные с ним аргументы заменяются реальными аргументами, указанными в коде программы. Такие макроопределения действуют подобно функциям. Рассмотрим пример.
/* Использование "функциональных" макроопределений. */
#include
using namespace std;
#define MIN(a, b) (((a)<(b)) ? a : b)
int main()
{
int x, y;
x = 10;
y = 20;
cout << "Минимум равен: " << MIN(x, у);
return 0;
}
При компиляции этой программы выражение, определенное идентификатором MIN (а, b), будет заменено, но x и y будут рассматриваться как операнды. Это значит, что cout инструкция после компиляции будет выглядеть так.
cout << "Минимум равен: " << (((х)<(у)) ? х : у);
По сути, такое макроопределение представляет собой способ определить функцию, которая вместо вызова позволяет раскрыть свой код в строке.

Слайд 184

Макроопределения, действующие как функции, — это макроопределения, которые принимают аргументы.
Кажущиеся избыточными круглые скобки, в

Макроопределения, действующие как функции, — это макроопределения, которые принимают аргументы. Кажущиеся избыточными
которые заключено макроопределение MIN, необходимы, чтобы гарантировать правильное восприятие компилятором заменяемого выражения. На самом деле дополнительные круглые скобки должны применяться практически ко всем макроопределениям, действующим подобно функциям. Нужно всегда очень внимательно относиться к определению таких макросов; в противном случае возможно получение неожиданных результатов. Рассмотрим, например, эту короткую программу, которая использует макрос для определения четности значения.
// Эта программа дает неверный ответ.
#include
using namespace std;
#define EVEN(a) a%2==0 ? 1 : 0
int main()
{
if(EVEN(9+1)) cout << "четное число";
else cout << "нечетное число ";
return 0;
}
Эта программа не будет работать корректно, поскольку не обеспечена правильная подстановка значений. При компиляции выражение EVEN(9+1) будет заменено следующим образом.
9+1%2==0 ? 1 : 0
Напомню, что оператор "%" имеет более высокий приоритет, чем оператор"+". Это значит, что сначала выполнится операция деления по модулю (%) для числа1, а затем ее результат будет сложен с числом9, что (конечно же) не равно 0. Чтобы исправить ошибку, достаточно заключить в круглые скобки аргумент a в макроопределенииEVEN, как показано в следующей (исправленной) версии той же программы.

Слайд 185

// Эта программа работает корректно.
#include
using namespace std;
#define EVEN(a) (a)%2==0 ? 1

// Эта программа работает корректно. #include using namespace std; #define EVEN(a) (a)%2==0
: 0
int main()
{
if(EVEN(9+1)) cout << "четное число";
else cout << "нечетное число";
return 0;
}
Теперь сумма 9+1 вычисляется до выполнения операции деления по модулю. В общем случае лучше всегда заключать параметры макроопределения в круглые скобки, чтобы избежать непредвиденных результатов, подобных описанному выше.
Использование макроопределений вместо настоящих функций имеет одно существенное достоинство: поскольку код макроопределения расширяется в строке, и нет никаких затрат системных ресурсов на вызов функции, скорость работы вашей программы будет выше по сравнению с применением обычной функции. Но повышение скорости является платой за увеличение размера программы (из-задублирования кода функции).
Важно! Несмотря на то что макроопределения все еще встречаются в C++-коде, макросы, действующие подобно функциям, можно заменить спецификатором inline, который справляется с той же ролью лучше и безопаснее. (Вспомните: спецификатор inline обеспечивает вместо вызова функции расширение ее тела в строке.) Кроме того, inline-функции не требуют дополнительных круглых скобок, без которых не могут обойтись макроопределения. Однако макросы, действующие подобно функциям, все еще остаются частью С++-программ, поскольку многиеС/С++-программистыпродолжают использовать их по привычке.

Слайд 186

Директива #еrror

Директива #error отображает сообщение об ошибке.
Директива #error дает указание компилятору остановить компиляцию. Она

Директива #еrror Директива #error отображает сообщение об ошибке. Директива #error дает указание
используется в основном для отладки. Общий формат ее записи таков.
#error сообщение
Обратите внимание на то, что элемент сообщение не заключен в двойные кавычки. При встрече с директивой #error отображается заданное сообщение и другая информация (она зависит от конкретной реализации рабочей среды), после чего компиляция прекращается. Чтобы узнать, какую информацию отображает в этом случае компилятор, достаточно провести эксперимент.

Слайд 187

Директива #include

Директива #include включает заголовочный или другой исходный файл.
Директива препроцессора #include обязывает компилятор включить либо стандартный

Директива #include Директива #include включает заголовочный или другой исходный файл. Директива препроцессора
заголовок, либо другой исходный файл, имя которого указано в директиве #include. Имя стандартных заголовков заключается в угловые скобки. Например, эта директива
#include
Включает стандартный заголовок для векторов.
При включении другого исходного файла его имя может быть указано в двойных кавычках или угловых скобках. Например, следующие две директивы обязывают C++ прочитать и скомпилировать файл с именем sample.h:
#include
#include "sample.h"
Если имя файла заключено в угловые скобки, то поиск файла будет осуществляться в одном или нескольких специальных каталогах, определенных конкретной реализацией.
Если же имя файла заключено в кавычки, поиск файла выполняется, как правило, в
текущем каталоге (что также определено конкретной реализацией). Во многих случаях это означает поиск текущего рабочего каталога. Если заданный файл не найден, поиск повторяется с использованием первого способа (как если бы имя файла было заключено в угловые скобки).

Слайд 188

Директивы условной компиляции

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

Директивы условной компиляции Существуют директивы, которые позволяют избирательно компилировать части исходного кода.
процесс, именуемый условной компиляцией, широко используется коммерческими фирмами по разработке программного обеспечения, которые создают и поддерживают множество различных версий одной программы.
Директивы #if, #ifdef, #ifndef, #else, #elif и #endif — это директивы условной компиляции.
Главная идея состоит в том, что если выражение, стоящее после директивы #if оказывается истинным, то будет скомпилирован код, расположенный между нею и директивой #endif в противном случае данный код будет опущен. Директива #endif используется для обозначения конца блока #if.
Общая форма записи директивы #if выглядит так.
#if константное_выражение
последовательность инструкций
#endif
Если константное_выражение является истинным, код, расположенный непосредственно за этой директивой, будет скомпилирован.

Слайд 189

// Простой пример использования директивы #if.
#include
using namespace std;
#define MAX 100
int main()
{
#if

// Простой пример использования директивы #if. #include using namespace std; #define MAX
MAX>10
cout << "Требуется дополнительная память\n";
#endif
// ...
return 0;
}
При выполнении эта программа отобразит сообщение Требуется дополнительная память на экране, поскольку, как определено в программе, значение константы МАХ больше 10. Этот пример иллюстрирует важный момент: Выражение, которое стоит после директивы #if, вычисляется во время компиляции. Следовательно, оно должно содержать только идентификаторы, которые были предварительно определены, или константы. Использование же переменных здесь исключено.

Слайд 190

Поведение директивы #else во многом подобно поведению инструкции else, которая является частью языка C++:

Поведение директивы #else во многом подобно поведению инструкции else, которая является частью
она определяет альтернативу на случай невыполнения директивы #if. Чтобы показать, как работает директива #else, воспользуемся предыдущим примером, немного его расширив.
// Пример использования директив #if/#else.
#include
using namespace std;
#define MAX 6
int main()
{
#if MAX>10
cout << "Требуется дополнительная память.\n");
#else
cout << "Достаточно имеющейся памяти.\n";
#endif
// . . .
return 0;
}
В этой программе для имени МАХ определено значение, которое меньше 10, поэтому #if- ветвь кода не скомпилируется, но зато скомпилируется альтернативная #else-ветвь. В результате отобразится сообщение Достаточно имеющейся памяти..
Обратите внимание на то, что директива #else используется для индикации одновременно как конца #if-блока, так и начала #еlse-блока. В этом есть логическая необходимость, поскольку только одна директива #endif может быть связана с директивой #if.

Слайд 191

Директива #elif эквивалентна связке инструкций else-if и используется для формирования многозвенной схемы if-else-if,представляющей несколько вариантов

Директива #elif эквивалентна связке инструкций else-if и используется для формирования многозвенной схемы
компиляции. После директивы #elif должно стоять константное выражение. Если это выражение истинно, следующий блок кода скомпилируется, и никакие другие #elif- выражения не будут тестироваться или компилироваться. В противном случае будет проверено следующее по очереди #elif-выражение. Вот как выглядит общий формат использования директивы #elif.
#if выражение
последовательность инструкций
#elif выражение 1
последовательность инструкций
#elif выражение 2
последовательность инструкций
#еlif выражение 3
последовательность инструкций
// . . .
#elif выражение N
последовательность инструкций
#endif

Слайд 192

Например, в этом фрагменте кода используется идентификатор COMPILED_BY, который позволяет определить, кем компилируется

Например, в этом фрагменте кода используется идентификатор COMPILED_BY, который позволяет определить, кем
программа.
#define JOHN 0
#define BOB 1
#define TOM 2
#define COMPILED_BY JOHN
#if COMPILED_BY == JOHN
char who[] = "John";
#elif COMPILED_BY == BOB
char who[] = "Bob";
#else
char who[] = "Tom";
#endif

Слайд 193

Директивы #if и #elif могут быть вложенными. В этом случае директива #endif, #else или #elif связывается

Директивы #if и #elif могут быть вложенными. В этом случае директива #endif,
с ближайшей директивой #if или #elif. Например, следующий фрагмент кода совершенно допустим.
#if COMPILED_BY == BOB
#if DEBUG == FULL
int port = 198;
#elif DEBUG == PARTIAL
int port = 200;
#endif
#else
cout << "Боб должен скомпилировать код" << "для отладки вывода данных.\n";
#endif

Слайд 194

Директивы #ifdef и #ifndef

Директивы #ifdef и #ifndef предлагают еще два варианта условной компиляции, которые можно

Директивы #ifdef и #ifndef Директивы #ifdef и #ifndef предлагают еще два варианта
выразить как "если определено"и "если не определено"соответственно.
Общий формат использования директивы #ifdef таков.
#ifdef макроимя
последовательность инструкций
#endif
Если макроимя предварительно определено с помощью какой-нибудь директивы #define, то последовательность инструкций, расположенная между директивами #ifdef и #endif, будет скомпилирована.
Общий формат использования директивы #ifndef таков.
#ifndef макроимя
последовательность инструкций
#endif
Если макроимя не определено с помощью какой-нибудь директивы #define, то последовательность инструкций, расположенная между директивами #ifdef и #endif, будет скомпилирована.
Как директива #ifdef, так и директива #ifndef может иметь директиву #else или #elif.

Слайд 195

Директива #undef

Директива #undef используется для удаления предыдущего определения некоторого макроимени. Ее общий формат таков.
#undef

Директива #undef Директива #undef используется для удаления предыдущего определения некоторого макроимени. Ее
макроимя
Рассмотрим пример.
#define TIMEOUT 100
#define WAIT 0
// . . .
#undef TIMEOUT
#undef WAIT
Здесь имена TIMEOUT иWAIT определены до тех пор, пока не выполнится директива
#undef.
Основное назначение директивы #undef — разрешить локализацию макроимен для тех частей кода, в которых они нужны.

Слайд 196

Использование оператора defined

Помимо директивы #ifdef существует еще один способ выяснить, определено ли в программе

Использование оператора defined Помимо директивы #ifdef существует еще один способ выяснить, определено
некоторое макроимя. Для этого можно использовать директиву #if в сочетании с оператором времени компиляции defined. Например, чтобы узнать, определено ли макроимя MYFILE, можно использовать одну из следующих команд препроцессорной обработки.
#if defined MYFILE
или
#ifdef MYFILE
При необходимости, чтобы реверсировать условие проверки, можно предварить оператор defined символом"!". Например, следующий фрагмент кода скомпилируется только в том случае, если макроимя DEBUG не определено.
#if !defined DEBUG
cout << "Окончательная версия!\n";
#endif

Слайд 197

Директива #line

Директива #line изменяет содержимое псевдопеременных _ _LINE_ _ и _ _FILE_

Директива #line Директива #line изменяет содержимое псевдопеременных _ _LINE_ _ и _
_.
Директива #line используется для изменения содержимого псевдопеременных_ _LINE_ _ и_ _FILE_ _, которые являются зарезервированными идентификаторами (макроименами). Псевдопеременная_ _LINE_ _ содержит номер скомпилированной строки, а псевдопеременная_ _FILE_ _— имя компилируемого файла. Базовая форма записи этой команды имеет следующий вид.
#line номер "имя_файла"
Здесь номер — это любое положительное целое число, а имя_файла — любой допустимый идентификатор файла. Значение элемента номер становится номером текущей исходной строки, а значение элемента имя_файла— именем исходного файла. Имя_файла
— элемент необязательный. Директива #line используется, главным образом, в целях отладки и в специальных приложениях.
Например, следующая программа обязывает начинать счет строк с числа 200. Инструкция cout отображает номер 202, поскольку это — третья строка в программе после директивной инструкции #line 200.
#include
using namespace std;
#line 200 // Устанавливаем счетчик строк равным 200.
int main() // Эта строка сейчас имеет номер 200.
{// Номер этой строки равен 201.
cout << _ _LINE_ _;// Здесь выводится номер 202.
return 0;
}

Слайд 198

Директива #pragma

Работа директивы #pragma зависит от конкретной реализации компилятора. Она позволяет выдавать компилятору различные

Директива #pragma Работа директивы #pragma зависит от конкретной реализации компилятора. Она позволяет
инструкции, предусмотренные создателем компилятора. Общий формат его использования таков.
#pragma имя
Здесь элемент имя представляет имя желаемой #pragma-инструкции. Если указанное имя не распознается компилятором, директива #pragma попросту игнорируется без сообщения об ошибке.
Важно! Для получения подробной информации о возможных вариантах использования директивы #pragma стоит обратиться к системной документации по используемому вами компилятору. Вы можете найти для себя очень полезную информацию. Обычно #pragma-инструкции позволяют определить, какие предупреждающие сообщения выдает компилятор, как генерируется код и какие библиотеки компонуются с вашими программами.

Слайд 199

Операторы препроцессора "#" и "##"

В C++ предусмотрена поддержка двух операторов препроцессора: "#" и"##". Эти

Операторы препроцессора "#" и "##" В C++ предусмотрена поддержка двух операторов препроцессора:
операторы используются совместно с директивой #define. Оператор"#" преобразует следующий за ним аргумент в строку, заключенную в кавычки. Рассмотрим, например, следующую программу.
#include
using namespace std;
#define mkstr(s) # s
int main()
{
cout << mkstr(Я в восторге от C++);
return 0;
}
Препроцессор C++ преобразует строку
cout << mkstr(Я в восторге от C++);
в строку
cout << "Я в восторге от C++";

Слайд 200

##

Оператор используется для конкатенации двух лексем. Рассмотрим пример.
#include
using namespace std;
#define concat(a,

## Оператор используется для конкатенации двух лексем. Рассмотрим пример. #include using namespace
b) a ## b
int main()
{
int xy = 10;
cout << concat(x, y);
return 0;
}
Препроцессор преобразует строку
cout << concat (x, y);
в строку
cout << xy;