С++. Краткий курс лекций
Вид материала | Курс лекций |
- Краткий курс лекций, 182.24kb.
- Н. В. Рудаков Краткий курс лекций, 1552.23kb.
- В. Б. Аксенов Краткий курс лекций, 1098.72kb.
- Краткий курс лекций по медицинской паразитологии Часть Клещи, 643.33kb.
- Краткий курс лекций по философии учебно-методическое пособие для студентов всех специальностей, 2261.57kb.
- Методические указания: краткий курс лекций для студентов заочной формы обучения Санкт-Петербург, 1540.61kb.
- Краткий курс лекций учебной дисциплины «Методика преподавания начального курса математики», 631.78kb.
- И. М. Максимова теория государства и права краткий курс лекций, 2531.05kb.
- Ю. А. Фатеев логика Краткий курс лекций, 665.66kb.
- Краткий конспект лекций Кемерово 2002 удк: 744 (075), 1231.26kb.
Желакович И.М. С++. Краткий курс лекций.
С++. Краткий курс лекций.
Операции
Операции бывают унарные, бинарные, тернарная (1).
Различаются типов операндов и результата. Любая операция имеет результат. Результат – адресное выражение (l-value) или нет (r-value).
- Операция присваивания '=' . результат – значение левого операнда
v = e
a = b = c = d = e + 2; (1-2-3-4-5)
Составная операция присваивания
a op= b, здесь op - знак одной из бинарных операций:
+ - * / % >> << & | && ||.
адресное выражение вычисляется только один раз.
a += 2 означает a = a + 2
s /= a означает s = s / a
Вычисляется справа налево
- Арифметические операции
Унарные + и -
Бинарными арифметическими операциями являются + - * / %.
При делении целых дробная часть отбрасывается.
Так, 10/3 дает 3, в то время как 10/3.0 дает 3.33333...
Операция a % b применяется только к целым операндам и дает остаток от деления a на b, так
10 % 3 дает 1,
2 % 3 дает 2,
12 % 2 дает 0.
- Операции отношения
true – 1, false - 0
=> > <= < == (равно), != (не равно)
Результат – 1 (истина) = true или 0 (ложь) false
- Логические операции
логическое НЕ, ! (отрицание) унарная;
логическое И , && (конъюнкция - умножение) если оба 1 = 1, иначе - 0;
логическое ИЛИ, || (дизъюнкция - сложение), если оба 0 = 0, иначе 1.
Операнды - целых, плавающих типов (1 иди 0).
Результат - 0 или 1 типа int.
- Битовые операции
& И
| ИЛИ
Искл.ИЛИ (если разные = 1, иначе – 0)
~ НЕ
Операции выполняются над битами попарно без переноса разряда:
- Сдвиги
<< >>
Операнды – целое. сдвиг влево и вправо левого операнда на число битов, заданных правым операндом.
- Операции инкремента ++ и декремента --
унарные. изменяют значение операнда на 1. Операнд должен быть целого или плавающего типа (или типа указатель) – не константа
Префиксная и постфиксная форма
- Тернарная или условная операция
Тернарная операция, т.е. операция с тремя операндами, имеет форму
операнд1 ? операнд2 : операнд3
Если операнд1 != 0, то вычисляется операнд2 иначе операнд3.
Приоритеты операций и порядок увеличения
Приоритет | Операция | Примечание | Порядок выполнения |
1 | ::. [ ] ( ) ( int) | разрешение контекста, извлечение индексирование массива вызов функции преобразование типа | слева - направо слева - направо слева - направо слева - направо |
2 | ++ -- ~ ! - + & * new,delete sizeof | унарный -, унарный + получение адреса разрешение указателя работа с динамической памятью определение размера | справа - налево справа - налево справа - налево справа - налево справа - налево справа - налево |
3 | * / % | умножение деление остаток | слева - направо слева - направо слева - направо |
4 | * .* | извлечение | слева - направо |
5 | + - | бинарное сложение бинарное вычитание | слева - направо слева - направо |
6 | << >> | сдвиги | слева - направо |
7 | < <= > => | сравнение | слева - направо |
8 | = = != | равно не равно | слева - направо |
9 | & | побитовое И | слева - направо |
10 | | XOR (исключающее ИЛИ) | слева - направо |
11 | | | побитовое ИЛИ | слева - направо |
12 | && | И - логическое | слева - направо |
13 | || | ИЛИ - логическое | слева - направо |
14 | ?: | тернарная операция | справа - налево |
15 | = *= /= %= += и т.д. | операция присвоения | справа - налево |
16 | , | следование | слева - направо |
Операторы
В С++ точка с запятой является признаком конца оператора.
- Пустой оператор состоит из ;
где по правилам языка должен находиться какой-либо оператор, а по логике программы там ничего выполнять не надо.
- Оператор-выражение
Любое выражение, за которым следует ; является оператором. Такой оператор называется оператор-выражение.
Примеры:
i++;
a = b + c;
c + = (a < b)? a : b;
x+y; // Здесь результат не используется
// и будет выдано предупреждение.
- Блок
там, где синтаксис языка требует наличие лишь одного оператора, а логика программы - сразу нескольких. Точка с запятой после скобок не ставится.
{i=5; c=sin(i*x); c++;} // Это блок.
- Объявления
В С++ объявления являются операторами языка и могут стоять там, где возможен любой другой оператор С++:
s = 0.3; d/=s; int k = 5;
d = s+2*k;
double f=s+d; f*=k;
- Условный оператор
Имеется две формы условного оператора:
1) if (выражение) оператор1;
2) if (выражение) оператор1; else оператор2;
if (a > b) c = a - b; else c = b - a; // c=(a>b)? a-b : b-a;
if (i < j) i++; else {j = i - 3; i ++;}
- Оператор выбора switch
Этот оператор позволяет передать управление одному из нескольких помеченных метками операторов в зависимости от значения целочисленного выражения. Метки оператора switch имеют специальный вид:
switch (селектор)
{
case константа1:
[case константа2: ]
. . .
[оператор;]
. . .
[break;]
[case константа_m:]
. . .
[case константа_n:]
[оператор;]
. . .
[break;]
[default:] [операторы]
}
Здесь [] означают необязательную часть оператора, а ... говорит о том, что указанная конструкция может применяться сколько угодно раз. Блок после switch( ) называют телом оператора switch.
Схема выполнения оператора:
- Сначала вычисляется селектор (целое).
- Затем вычисленное значение селектора последовательно сравнивается с константным выражением, следующим за case.
- Если селектор равен какому-либо константному выражению, стоящему за case, то управление передается оператору, помеченному соответствующим оператором case.
- Если встречается break - осуществляется немедленный выход из тела оператора switch. (иначе операторы выполняются подряд)
- Если селектор не совпадает ни с одной меткой варианта, то управление передается на оператор, помеченный словом default.
- Если default отсутствует, то управление передается следующему за switch оператору.
Пример 1:
int i;
cout<<"Введите оценку\n";
cin>>i;
switch (i)
{
case 1:
case 2: cout << "неудовлетворительно";
break;
case 3: cout << "удовлетворительно";
case 4: cout<< "хорошо";
break;
case 5: cout<<"отлично"; break;
default: cout<<"некорректная оценка";
}
- Оператор цикла while с предусловием
while (выражение) оператор;
Выполняется до тех пор, пока выражение истинно
- Цикл с постусловием do-while
do оператор; while (выражение);
Также выполняется пока условие истинно, но обязательно хотя бы 1 раз.
- Оператор for
for ( оператор1; выражение1; выражение2 ) оператор2;
Выполняется пока выражение 1 истинно.
Иногда оператор1 называют инициализатором цикла, а выражение1 - реинициализатором.
Любая из трех частей может быть опущена (выражение 1 в этом случае считается истинным).
- Оператор безусловного перехода
Оператор безусловного перехода имеет вид
goto метка;
Метка - это имя, за которым следует ':'.
- Оператор break
Этот оператор осуществляет выход из тела цикла for, while, do-while или оператора switch, в котором он появился. При этом управление передается на первый оператор после цикла.
Оператор не может обеспечить выход сразу из двух или более вложенных циклов.
- Оператор continue
Переход к следующей итерации цикла.
Пример вывода четных чисел:
for ( int num = 0; num < 100; num++ )
{
if ( num % 2 ) continue;
cout << num << "\n";
}
- Оператор return
Этот оператор завершает выполнение функции, в которой он задан, и возвращает управление в вызывающую функцию.
Управление передается в вызывающую функцию в точку, непосредственно следующую за вызовом.
Если return присутствует в функции main(), то он вызывает прерывание выполнения программы.
Массивы
Массив – это последовательность однотипных элементов, имеющих единый идентификатор и хранящихся в смежных ячейках памяти.
Единственный оператор для массива – индексация []. Начинается с 0. Индекс – всегда целое выражение.
Объявление массивов:
int massiv[5];
float d[4];
Размер массива при объявлении – всегда целое константное выражение. Нельзя объявить массив
Массив можно инициализировать одним из трех способов:
- при создании массива — используя инициализацию по умолчанию (этот метод применяется только для глобальных и статических массивов );
- при создании массива — явно указывая начальные константные значения.
- в процессе выполнения программы — путем записи данных в массив.
Двумерный массив рассматриваются как массив элементов, каждый из которых является одномерным массивом.
int matrix[3][4][2]
Строковые массивы.
В C++ нет строкового типа данных. Строка представляется в виде массива символов (char). Каждый символ хранится в отдельной ячейке массива, а в последней ячейке содержится null-символ "\0".
Длина массива должна учитывать "\0".
char s[5];
s[0] = 's';
s[1]='h';
s[2] = 'i';
s[3]='p';
s[4]='\0';
char s[] = {'s','h','i','p','\0'}; // можно не задавать размер
s = "ship"; // в этом случае null-символ будем добавлен автоматически
Функции для работы со строками берем из библиотеки
sprintf(str2,"***%s***",str1); // форматирование строки в строку
strcpy(str1,"Good "); // копирование
strcat(str1," morning!");// сцепление
Указатели
Указатель (pointer) - это переменная, содержащая адрес другой переменной (объекта). Точнее - адрес первого байта. Это дает возможность косвенного доступа к этому объекту через указатель. В 32-разрядных приложениях адрес занимает 4 байта. Следовательно, любой указатель имеет длину 4 байта. Принято соглашение, по которому имя переменной-указателя должно начинаться с буквы p. Наименьшая адресуемая единица памяти – 1 байт.
int *pa, *pb, *pc; // объявление нескольких указателей одного типа
float *pb;
Тип указателя должен совпадать с типом переменной, адрес которой он хранит.
Инициализация
Нельзя использовать в программе указатель, значение которого не определено (но ошибки это не вызовет).
Можно проинициализировать при объявлении:
double *px = 0; // это указатель в никуда
double *px = NULL; // тоже самое, но более грамотно
double y;
double *py = &y; // здесь адрес y
Контроль за типом указателя в С более строгий, чем за типом переменной:
- &.
px = &x; // взятие адреса
- *
Унарная операция * называется операцией разыменования (разадресации, операцией разрешения адреса). Эта операция рассматривает свой операнд как адрес и обращается по этому адресу, чтобы извлечь объект, содержащийся по этому адресу.
z = *px + *py;
*pz = *px + *py;
- =
px = &x;
px = py;
py = 0; // указатель в никуда
Операции с указателями.
Название | Знак | Пояснение |
Взятие адреса | & | Получить адрес переменной |
Разыменование | * | Получить значение переменной по адресу |
Присваивание | = | Присвоить указателю адрес переменной или 0 |
Инкремент | ++ | Увеличить указатель на целое значение (на след. элемент массива) |
Декремент | -- | Уменьшить указатель на целое значение (на пред. элемент массива) |
Сложение | + | Увеличить указатель на целое значение и присвоить другому указателю |
Сложение с замещением | += | Увеличить существующий указатель на целое значение |
Вычитание | – | Уменьшить указатель на целое значение или на значение другого указателя, если оба указывают на один и тот же массив, и присвоить третьему указателю. |
Вычитание с замещением | – | Уменьшить указатель на целое значение или на значение другого указателя, если оба указывают на один и тот же массив. |
Отношения | == != < <= > >= | Сравнение указателей – истина или ложь |
Выделение памяти | new | Получить указатель на начало выделенного блока памяти |
Освобождение памяти | delete | Освободить выделенный блок памяти и сделать указатель недоступным |
Указатели и массивы
Имя массива само по себе является адресом.
int x[]= {1,2,3,4,5,6,7,8,9,10,11,12};
int *px = &x[0]; // берем адрес первого элемента
*px = x; // не требуется использовать &
int y[][2] = {1,2,3,4,5,6,7,8,9,10,11,12};
int *py = &y[0][0]; // берем адрес первого элемента
*py = y; // нельзя
*py = y[0]; // можно
// указатель можно нарастить
py++; // сдвиг на 1 элемент массива (вот зачем нужен тип)
px += 4;
cout << px; // выведет адрес
cout << *px; // выведет значение
// указатели можно сравнивать, если они указывают на 1 массив
px = &x[0];
py = &x[5];
if (px < py) cout << "*"; // *
// указатели можно вычитать, если они указывают на 1 массив
cout << py – px; // 5
cout << px – py; // -5
Работа со строками с указателемя
Строки интерпретируются как обычные массивы с размером каждого элемента в 1 символ (1 байт). Имя строки – это адрес. Длина строки – до 0-символа
Вывод такого же строкового массива:
char s[10] = "irina";
char *p = s;
while (*p) // очень удобно – до \0
cout << *(p++) << endl;
i
r
i
n
a
Но если убрать разыменование:
char s[10] = "irina";
char *p = s;
while (*p)
cout << p++ << endl;
irina
rina
ina
na
a
Указатели на указатели
В языке С есть возможность описать переменные-указатели, указывающие на другие указатели.
ppi pi i
0x00123030 0x00122020 0x00121010
int i = 10;
int *pi = &i;
int **ppi = π
int ***pppi = &ppi;
Указатели на константу
const double *pPI = &PI; //указатель на константу
Значение по этому указателю менять нельзя, сам указатель – можно
Константные указатели
int Nomer = 0;
int *const pNomer = &Nomer;
Этот указатель всегда привязан к одной и той же переменной. Но менять саму переменную можно
Константные указатели на константу
const double *const pi_ptr = π // ничего менять нельзя
Указатели на void
Если заранее неизвестно, на какой именно тип данных будет ссылаться указатель, его описывают как void. Это не совсем тоже самое, что тип функции void:
void main() // функция ничего не возвращает (пусто)
Указатель на void ссылается на любой тип данных:
void *pobject;
int a, *pa = &a; float c; double d;
pobject = pa; pobject = &c; pobject = &d;
pa = pobject; // ошибка
Но с ним не работают арифм. операции.
Динамическое распределение памяти
В программе возможно существование переменных, размер которых при компиляции неизвестен. Для них необходимо самостоятельно выделять память из свободной области. Свободная область занимает верхнюю часть памяти программы и растет вверх.
Для работы с динамической памятью используются унарные операторы new и delete.
Оператор new выделяет блок памяти и возвращает указатель на первую ячейку памяти этого блока. Если new не в состоянии найти необходимое пространство свободной памяти, он вернет указатель NULL.
указатель = new тип;
указатель = new тип (значение);
int *pi;
pi = new int;
float *pf = new float (-3.5);
Освобождение памяти:
delete указатель;
Ни с указателем, ни с блоком памяти ничего не происходит. Просто память становится свободной, а указатель теряет силу. Его разыменование может иметь непредсказуемые последствия. Автоматически указатель не обнуляется, и более безопасно сделать это вручную:
delete pf;
pf = NULL;
Становится возможным создавать массивы, размер которых становится известен лишь в процессе выполнения программы.
указатель = new тип_массива [размер];
delete [] указатель;
int size; cout << "Vvedite razmer massiva "; cin >> size;
int *pmassiv = new int[size];
for (int i=0; i < size; i++)
{
cout << "Vvedite element ";
cin >> *pmassiv[i];
}
Функция
Функция - это самостоятельная единица программы, спроектированная для реализации конкретной задачи.
Прототип функции
Прежде всего, функцию необходимо объявить. Объявление функции, аналогично объявлению переменной, определяет имя функции и ее тип – типы и количество ее аргументов и тип возвращаемого значения.
double sqrt(double x); //функция sqrt с аргументом – double и результатом double
int sum(int a, int b, int c); // функция от 3 целых аргументов возвращает целое
Прототип функции аналогичен заголовку (первой строке в определении), но заканчивается точкой с запятой.
Определение (описание) функции имеет вид:
тип имя ( список описаний аргументов ){ операторы }
Вызов такой функции имеет вид:
имя ( список фактических аргументов );
Передача аргументов
В приведенных выше примерах происходит передача аргументов по значению.
int power (int x, int n)
void swap (int* x, int* y) // по ссылке
При передаче массива передается указатель на массив (1-ый элемент) и размер (размеры) массива
Некоторые аргументы могут быть объявлены по умолчанию.
Перегрузка (переопределение) функций
В С++ можно переопределять имена функций и использовать одно и то же имя для нескольких функций с различным типом или числом аргументов.
Пусть объявлены следующие функции:
int func(int, int);
int func(char, double);
int func(long, double);
int func(char*, int);
При вызове функции
int x = func(5,5); // 1
x = func('f',3.7); //2
si не найдено – будут рассматриваться преобразования типов и аргументы по умолчанию
Указатели на функции
Указатель на функцию определим следующим образом:
Тип_функции (*имя_указателя) (список параметров);
Например,
int (*fptr) (double);
Пример:
void f1(void){ cout<<"\n Выполняется f1().";}
void f2(void){ cout<<"\n Выполняется f2().";}
void main()
{
void (*ptr)(void);
ptr = f2;
(*ptr)(); //вызов функции f2();
ptr = f1;
(*ptr)(); //вызов f1();
ptr(); //альтернативная запись!
}
Результат:
Выполняется f2().
Выполняется f1().
Выполняется f1().
Массивы указателей на функции
А вот каким образом описывается массив указателей на функцию:
char* (*MyPtArray[3]) (int, int*, float);
Здесь описан массив указателей из 3 элементов. Инициализация массива указателей возможна лишь после объявления трёх однотипных функций:
extern char* MyFF1 (int, int*, float);
extern char* MyFF2 (int, int*, float);
extern char* MyFF3 (int, int*, float);
char* (*MyPtArray[3]) (int, int*, float) =
{
MyFF1,
MyFF2,
MyFF3
}; // Инициализация массива указателей.
Вызов функции (например, MyFF3()) с помощью элемента массива указателей можно осуществить следующим образом:
char* MyPointChar = MyPtArray[2](7,7,NULL,7.7);
Указатель на функцию как параметр функции
Указатель на функцию может быть описан как параметр функции:
void MonitorF(int,int*,float,char*(*)(int,int*,float));
// Торжество абстрактного описателя!
И этому параметру можно присвоить значение (значение по умолчанию):
void MonitorF(int, int*,float, char*(*)(int,int*,float)= MyFF1);
Функция, что используемая для инициализации последнего параметра функция должна быть к моменту инициализации, по крайней мере, объявлена.
Если в выражении имя функции возникает не в положении имени функции в вызове, то генерируется указатель на функцию. Так, для передачи одной функции другой можно написать
a = f1(f2,f3);
Структуры
Структура - это набор из одной или более переменных, возможно различных типов, сгруппированных под одним именем для удобства обработки.
Дата состоит из нескольких частей таких, как день, месяц, и год, и, возможно, день недели и имя месяца. Эти пять переменных можно объединить в одну структуру вида:
struct date {
int day;
int month;
int year;
int week_day;
char mon_name[4];
} x,y,z;
Обращение к отдельному элементу структуры:
x.day = 22;
y.month = 1; //Операция указания элемента структуры "."
Структуры можно присаивать:
x = y;
Но нельзя сравнивать:
Структуры могут быть вложенными – учетная карточка служащего может фактически выглядеть так:
struct person {
char name[20];
char address[50];
salary; /* зарплата */
struct date birthdate; /* дата рождения */
struct date hiredate; /* дата поступления на работу */
};
Если мы определим emp как
struct person emp;
то
emp.birthdate.month
будет ссылаться на месяц рождения. Операция указания члена структуры "." ассоциируется слева направо.
Массив структур:
person group [20];
group[5].birthdate.month = 8;
Указатель на структуру аналогичен указателю на любой другой тип данных.
person *pp = group;
pp++; // сдвиг на 1 элемент массива
cout << (*pp). birthdate.month; // обращение к массиву по указателю
cout << pp-> birthdate.month; // то же самое, но короче и нагдяднее