- •Функции
- •Scanf() - чтение данных из потока
- •47. Конструкторы и деструкторы
- •48. Перегрузка операторов и функций
- •51. Исключения (exceptions)
- •54. Одноименные поля в производном и базовых классах
- •55. Виртуальные функции
- •56. Абстрактные классы. Чистые виртуальные функции.
- •57. Виртуальные конструкторы
0.
1.
2.
3.
4.
5.
6. Бистабильные схемы (Триггеры) — класс электронных устройств, обладающих способностью длительно находиться в одном из двух устойчивых состояний и чередовать их под воздействием внешних сигналов. Каждое состояние триггера легко распознаётся по значению выходного напряжения. По характеру действия триггеры относятся к импульсным устройствам — их активные элементы (транзисторы, лампы) работают в ключевом режиме, а смена состояний длится очень короткое время.
Отличительной особенностью триггера как функционального устройства является свойство запоминания двоичной информации. Под памятью триггера подразумевают способность оставаться в одном из двух состояний и после прекращения действия переключающего сигнала. Приняв одно из состояний за «1», а другое за «0», можно считать, что триггер хранит (помнит) одинразряд числа, записанного в двоичном коде.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16. Основные концепции языков программирования
- Обоснованно выбирать язык программирования для реализации конкретного проекта;
- Разрабатывать более эффективные алгоритмы; - Систематически пополнять набор полезных языковых конструкций; - Ускорять изучение новых языков программирования;
- Использовать полученные знания как методологическую основу для разработки новых языков программирования;
-Получить базовые знания, необходимые для разработки трансляторов для языков программирования, поддерживающих разные вычислительные модели.
17. Парадигмы языков программирования
- Императивная; (Algol, BASIC, FORTRAN, PL/1, Ada, Pascal, C, C++, Java)
- Функциональная; (Lisp)
- Декларативная; (Prolog)
- Объектно-ориентированная; (Java, C++, Object Pascal, Smalltalk)
18. Критерии оценки языков программирования
- Понятность;
- Надежность;
- Гибкость;
- Простота;
- Естественность;
- Мобильность;
- Стоимость.
1)Понятность
- Уменьшаются требования к документированию проекта, если текст программы является центральным элементом документирования;
- Понятность конструкций языка позволяет легче понимать программу и, следовательно, быстрее находить ошибки;
- Высокая степень понятности конструкций языка позволяет легче сопровождать программу.
2) Надежность
-
Чем раньше при разработке программы обнаружена ошибка, тем меньше стоимость самого проекта;
-
Трансляция может быть выполнена на любой машине, воспринимающей входной язык, в то время как тестирование оттранслированной программы должно выполняться на целевой машине либо с использованием программ интерпретации, специально разработанных для тестирования
3) Гибкость
- Гибкость языка проявляется в том, сколько возможностей он предоставляет программисту для выражения всех операций, которые требуются в программе, не заставляя его прибегать к вставкам ассемблерного кода или различным ухищрениям.
4) Простота
-
Экономия понятий языка предполагает использование минимального числа понятий;
-
Ортогональность понятий означает, что между ними не должно быть взаимного влияния;
-
Единообразие понятий требует согласованного, единого подхода к описанию и использованию понятий.
5) Естественность
- Язык должен содержать такие структуры данных, управляющие структуры и операции, а также иметь такой синтаксис, которые позволяли бы отражать в программе логические структуры, лежащие в основе реализуемого алгоритма.
6)Мобильность
- Язык, независимый от аппаратуры, предоставляет возможность переносить программы с одной платформы на другую с относительной легкостью.
7) Стоимость
-
Стоимость обучения языку;
-
Стоимость создания программы;
-
Стоимость трансляции программы;
-
Стоимость выполнения программы;
-
Стоимость сопровождения программы.
19. Объекты данных в языках программирования
-
Имена: идентификатор – строка символов, используемая для обозначения некоторой сущности в программе (переменные, типы, метки, подпрограммы, формальные параметры и др.).
-
Константы:
литералы
именованные константы
-
Переменные
Имя,
Адрес,
Значение,
Тип,
Время жизни,
Область видимости
20. Механизмы типизации
- Статические и динамические типы данных
Недостатки динамического связывания типов:
- Снижается возможность обнаружения транслятором ошибок по сравнению со статическим связыванием типов, т.к. в момент трансляции отсутствует информация о типах переменных;
- При реализации динамического связывания вся информация о типах переменных должна сохраняться в течение всего времени работы программы, что требует значительных дополнительных ресурсов памяти, связанных с необходимостью хранить данные различных типов;
- Динамическое связывание типов приводит к увеличению времени работы программы за счет программной реализации механизмов связывания
21. Виды типизации
-
Слабая
Char c;
с=7;
int i;
float x;
…
i=x;
k=x-i;
-
Строгая типизация
- Каждый объект данных обладает уникальным типом;
- Каждый тип определяет множество значений и множество операций;
- В каждой операции присваивания тип присваиваемого значения и тип переменной, которой присваивается значение, должны быть эквивалентны;
- Каждая примененная к объекту данных операция должна принадлежать множеству операций, допустимых для объектов данного типа;
- Преобразование типа должно задаваться явно, например i:=integer(x)
22. Производные типы
1)program sum(input, output);
var
temp_weight, sum_weight: integer;
i: integer;
begin
sum_weight := 0;
for i:=1 to 10 do
begin
read(temp_weight);
sum_weight:= sum_weight+tem_weight
end;
writeln(sum_weight);
end.
// sum_weight:= sum_weight+I;
2) program sum(input, output);
type
weight=integer;
index=integer;
var
temp_weight, sum_weight: weight;
i: index;
begin
sum_weight := 0;
for i:=1 to 10 do
begin
read(temp_weight);
sum_weight:= sum_weight+tem_weight
end;
writeln(sum_weight);
end.
// sum_weight:= sum_weight+I;
23. Время жизни переменных
-
Статические переменные
-
Автоматические переменные
-
Явные динамические переменные (проблема «висячего» указателя и потерянной динамической переменной)
-
Неявные динамические переменные
24. Область видимости переменных
-
Правила видимости переменных определяют, каким образом ссылки на переменные, объявленные вне выполняющейся в данный момент подпрограммы (блока) связаны с объявлениями этих переменных.
25. Типы данных
-
Тип данных – это некоторый класс объектов данных вместе с набором операций для создания и работы с ними. В каждом языке программирования имеется некоторый набор встроенных элементарных типов данных. Дополнительно язык может предоставить возможности, позволяющие программисту определять новые типы данных.
-
Под реализацией типа данных понимают 1) Способ представления объектов данных этого типа в памяти компьютера во время выполнения программы; 2) Способ представления операций, определенных для этого типа данных (комбинация аппаратных и программных средств, реализующих конкретные алгоритмы и процедуры над представлениями объектов данных заданного типа в памяти).
1) Числовые типы
-
Целый тип (C: int, short, long, char; Pascal: integer, word, longint);
-
Вещественный тип (real, float, double)
2) Логический тип
-
bool, boolean – fasle, true
Pascal:
var Found: boolean;
begin
Found:=TRUE;
end.
C:
bool b=true;
3) Символьный тип и символьные строки
-
Char
-
String
4) Перечислимые типы
Pascal:
type color = (white, red, green, blue, black);
var circle, square: color;
C:
enum days {sun, mon, tues, wed, thur, fri, sat} anyday;
enum gender {man, wom} pol;
anyday=sun;
pol=wom;
if (anyday==0 && pol==wom);
Операции, определенные для перечислимых типов:
-
сравнение: равно, не равно, больше, меньше, больше или равно, меньше или равно;
-
операция присваивания;
-
операция succ и pred
26. Векторы и массивы
Вектор (одномерный массив) – это структура данных, состоящая из фиксированного количества компонентов одного типа. Компонент вектора определяется путем задания индекса, который является целочисленным значением или элементом перечислимого типа
Вектор характеризуется атрибутами:
-
количество компонентов (размер вектора);
-
тип данных компонентов;
-
список значений индексов;
Pascal: var A: array [1..20] of real;
C: char name[20];
27. Записи
Записью (структурой) называют структуру данных, состоящую из фиксированного количества компонентов (полей), которые могут соответствовать различным типам. Компоненты записей обозначаются символическими именами (идентификаторами).
Запись характеризуется атрибутами:
-
количество компонентов записи;
-
тип данных каждого компонента;
-
идентификатор для каждого компонента
C:
-
struct book
-
{ char name[20];
-
char title[50];
-
int year;
-
float price
-
} child_book, my_book;
Pascal:
-
type Tbook= record
-
Name: array [1..20] of char;
-
Title: array [1..20] of char;
-
Year: integer;
-
Price: real;
-
end;
-
var child_book, my_book: Tbook;
28. Указатели
Указатели включаются в определение языка с целью обеспечения возможности конструирования произвольных структур данных из объектов разного типа. Над указателями определены следующие операции:
-
Операция создания объекта данных фиксированного размера. В памяти отводится место для нового объекта, создается указатель на этот объект, которому присваивается значение ссылки (адреса) на этот объект;
-
Операция разыменования – операция использует значение указателя для доступа к объекту данных, на который он ссылается;
1) int a,b;
int *pa;
a=1;
pa=&a;
b=*pa;
2) var pa: ^integer;
a,b: integer;
begin
a:=1; pa:=@a;
b:=pa^;
end.
29.
30. Структуры управления на уровне операторов
-
Композиция – операторы могут быть представлены в виде последовательности, выполняемой как единое целое;
-
Ветвление – две последовательности операторов могут быть альтернативными, при этом в каждом конкретном случае выполняется один из них;
-
Повторение – последовательность операторов может выполняться многократно или вообще не выполняться в зависимости от некоторого условия.
31.
32.
33.
34.
35.
36.
37.
Функции
Вызов функции - это оператор. У вызова функции есть приоритет – один из самых высоких. Список аргументов функции считают за один операнд, так что оператор оказывается бинарным (первый операнд - сама функция, второй - список ее аргументов).
Пример записи функции func:
double func(double param1, int param2)
{ return param1-0.1*param2; }
Сначала указан тип значения, которое функция возвращает - в данном случае это double. Затем после пробела следует имя функции - идентификатор, составленный по тем же правилам, что и для имен переменных. После имени функции в круглых скобках перечислены формальные параметры с указанием их типов.
Формальные параметры разделены запятыми. В нашей функции это param1 типа double и param2 типа int.
После круглых скобок со списком формальных параметров следует блок с телом функции - тот, который в фигурных скобках, причем в теле функции мы можем использовать формальные параметры как обычные переменные.
Определив функцию, мы можем ее неоднократно вызывать, задавая в
качестве фактических параметров нужные нам переменные или
значения. При этом мы можем использовать то значение, которое она
возвращает, а можем его игнорировать (если нам просто надо, чтобы
выполнились операторы в теле функции).
int i; double x, result; ... /* два вызова функции в выражении */ result = func(x,i) * func( i+x, 100 ); /* Вызываем еще раз, но игнорируем возвращаемое значение */ func(x, i);
38. Функции без возвращаемого значения
/* У этой функции нет возвращаемого значения */ void f() { ... return; }
В операторе return нет никакого значения, сразу после ключевого слова стоит точка с запятой.
Также можно написать void вместо списка параметров, если функции параметры не нужны:
int f(void) { ... return 0; }
39. Параметры и переменные
int i, j; /* У первой функции видны i, j файлового уровня. Кроме того, у нее есть формальный параметр k и локальная переменная result В процессе работы эта функция изменяет значение файловой переменной i */
int f1(int k) { int result; result = i*j + k; i += 100; return result; }
/* Во второй функции имя формального параметра совпадает с именем переменной i файлового уровня, при работе используется параметр, а не файловая переменная. */
int f2(int i)
{ /* i - параметр, j - файловая */ return i*j;
}
/* С третьей функцией аналогичная ситуация, что и со второй. Только на этот раз маскируется файловая переменная j, и не формальным параметром, а локальной переменной. */
int f3(int k)
{ int j; j=100; /* i - файловая, j - локальная */ return i*j + k;
}
Переменная j самого внутреннего блока маскирует не только файловую, но и локальную переменную из внешнего блока. */ int f4 (int k)
{ /* Объявляем переменную и сразу инициализируем */ int j=100; { /* Объявляем еще одну локальную с тем же именем, что у файловой и локальной из внешнего блока */ int j=10; /* i - файловая, j - локальная, причем из внутреннего блока */ return i*j + k; }
}
40. Необходимость инициализации переменных (автоматические переменные)
/* Файловая переменная без инициализации, будет равна 0 */ int s; int f() { /* Локальная без инициализации, содержит "мусор" */ int k; return k; } int main() { printf("%d\n", s); /* Всегда печатает 0 */ /* Невозможно предсказать, что увидим */ /* К тому же числа могут быть разными */ printf("%d\n", f()); ...; printf("%d\n", f()); return 0;
41. Статические переменные
int f() { static int i; return i; }
Перед обычным определением переменной модификатор типа – ключевое слово static. Теперь функция всегда возвращала бы 0 – локальные статические также, как и файловые, создаются один раз и инициализируются нулем, если только не задать другую инициализацию.
Эти переменные создаются один раз за время работы программы, и один раз инициализируются - либо нулем, либо тем значением, которое было задано. Поскольку они «живут» независимо от функции, значит в одном вызове функции в такую переменную можно что-то положить, а в следующем - это что-то использовать.
int f() { static int ncalls=1; /* Который раз мы эту функцию вызвали? */ printf("number of calls %d\n", ncalls++); ... }
Полезный «трюк», основанный на статических локальных переменных –
возможность выполнять какие-то дорогостоящие «подготовительные» операции
только один раз.
int func() { /* Неявная инициализация тоже дала бы 0, но правила хорошего тона требуют ... */ static int init_done=0; if (!init_done) { /* Здесь мы выполняем какую-то "дорогостоящую«, но разовую работу - например, считываем таблицу значений из файла. А потом указываем, что таблица прочитана и при следующих вызовах этого делать уже не нужно. */ read_table(); init_done = 1; } /* А в этом месте мы пользуемся табличными данными. */ ... }
42. Передача по значению
Передача параметра по значению" и "передача параметра по
ссылке".
#include <stdio.h> void f(int k) { k = -k; } int main() { int i = 1; f(i); printf("%d\n", i); return 0; }
43. Адреса и указатели
int i; double d; /* Функции передаются адреса переменных i и d. После вызова функции
адреса останутся прежними (pass-by-value), но значения могут измениться */ func( &i, &d ); ...
char *s; /* указатель на char */ int *pi; /* указатель на int */ void *pv; /* указатель на void */ char **av /* указатель на указатель на char */ /* Это указатель на функцию, которая возвращает int, а в качестве параметра ожидает char */ int (*pf)(char)
Указатели можно использовать не только для параметров, но и в
качестве возвращаемого значения функции. Вот как, например, выглядит
определение и вызов функции, возвращающей указатель на тип char
(такой тип используется для передачи текстовых строк).
char *genstr() { char *p; ... return p; }
char *s;
s = genstr();
44. Чем «опасны» указатели?
void f(int *p) { *p=1; } int main() { int i; int *ptr; f(ptr); ... return 0; }
Что делать, если указатель создан, но пока не известно, какой адрес в него записать? Для этого есть специальное значение указателя - в C это символьная константа NULL, в C++ - 0.
#include <stdlib.h> main() { char *p=NULL; ...}
void f(int *p) { if (p != NULL) *p=1; else { printf("Help!!! NULL pointer in f()\n"); abort(); } }
int main() { int i; int *ptr=NULL; f(ptr); ... return 0;
}
45. Ввод-вывод
#include <stdio.h> int main() { /* Печатаем целое число и строку */ printf("integer=%d, string=%s \n", 10, "hello"); }
#include <stdio.h> int main() { int i; char c; /* Вводим целое число и символ */ scanf("%d %c", &i, &c); }