С++. Краткий курс лекций

Вид материалаКурс лекций

Содержание


Арифметические операции
Операции отношения
Логические операции
Тернарная или условная операция
Оператор выбора switch
Оператор цикла while с предусловием
Оператор for
Оператор безусловного перехода
Оператор continue
Оператор return
Массив – это последовательность однотипных элементов, имеющих единый идентификатор и хранящихся в смежных ячейках памяти.
Строковые массивы.
Указатель (pointer) - это переменная, содержащая адрес другой переменной (объекта).
Операции с указателями.
Указатели и массивы
Работа со строками с указателемя
Указатели на указатели
Указатели на константу
Константные указатели на константу
Динамическое распределение памяти
...
Полное содержание
Подобный материал:

Желакович И.М. С++. Краткий курс лекций.

С++. Краткий курс лекций.

Операции


Операции бывают унарные, бинарные, тернарная (1).

Различаются типов операндов и результата. Любая операция имеет результат. Результат – адресное выражение (l-value) или нет (r-value).
  1. Операция присваивания '=' . результат – значение левого операнда

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

Вычисляется справа налево
  1. Арифметические операции

Унарные + и -

Бинарными арифметическими операциями являются + - * / %.

При делении целых дробная часть отбрасывается.

Так, 10/3 дает 3, в то время как 10/3.0 дает 3.33333...

Операция a % b применяется только к целым операндам и дает остаток от деления a на b, так

10 % 3 дает 1,

2 % 3 дает 2,

12 % 2 дает 0.
  1. Операции отношения

true – 1, false - 0

=> > <= < == (равно), != (не равно)

Результат – 1 (истина) = true или 0 (ложь) false
  1. Логические операции

логическое НЕ, ! (отрицание) унарная;

логическое И , && (конъюнкция - умножение) если оба 1 = 1, иначе - 0;

логическое ИЛИ, || (дизъюнкция - сложение), если оба 0 = 0, иначе 1.

Операнды - целых, плавающих типов (1 иди 0).

Результат - 0 или 1 типа int.
  1. Битовые операции

& И

| ИЛИ

Искл.ИЛИ (если разные = 1, иначе – 0)

~ НЕ


Операции выполняются над битами попарно без переноса разряда:
  1. Сдвиги

<< >>

Операнды – целое. сдвиг влево и вправо левого операнда на число битов, заданных правым операндом.
  1. Операции инкремента ++ и декремента --

унарные. изменяют значение операнда на 1. Операнд должен быть целого или плавающего типа (или типа указатель) – не константа

Префиксная и постфиксная форма


  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

,

следование

слева - направо



Операторы


В С++ точка с запятой является признаком конца оператора.

  1. Пустой оператор состоит из ;

где по правилам языка должен находиться какой-либо оператор, а по логике программы там ничего выполнять не надо.
  1. Оператор-выражение

Любое выражение, за которым следует ; является оператором. Такой оператор называется оператор-выражение.

Примеры:

i++;

a = b + c;

c + = (a < b)? a : b;

x+y; // Здесь результат не используется

// и будет выдано предупреждение.
  1. Блок

там, где синтаксис языка требует наличие лишь одного оператора, а логика программы - сразу нескольких. Точка с запятой после скобок не ставится.

{i=5; c=sin(i*x); c++;} // Это блок.
  1. Объявления

В С++ объявления являются операторами языка и могут стоять там, где возможен любой другой оператор С++:

s = 0.3; d/=s; int k = 5;

d = s+2*k;

double f=s+d; f*=k;
  1. Условный оператор

Имеется две формы условного оператора:

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 ++;}
  1. Оператор выбора 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<<"некорректная оценка";

}
  1. Оператор цикла while с предусловием

while (выражение) оператор;

Выполняется до тех пор, пока выражение истинно
  1. Цикл с постусловием do-while

do оператор; while (выражение);

Также выполняется пока условие истинно, но обязательно хотя бы 1 раз.
  1. Оператор for

for ( оператор1; выражение1; выражение2 ) оператор2;

Выполняется пока выражение 1 истинно.

Иногда оператор1 называют инициализатором цикла, а выражение1 - реинициализатором.

Любая из трех частей может быть опущена (выражение 1 в этом случае считается истинным).
  1. Оператор безусловного перехода

Оператор безусловного перехода имеет вид

goto метка;

Метка - это имя, за которым следует ':'.




  1. Оператор break

Этот оператор осуществляет выход из тела цикла for, while, do-while или оператора switch, в котором он появился. При этом управление передается на первый оператор после цикла.

Оператор не может обеспечить выход сразу из двух или более вложенных циклов.
  1. Оператор continue

Переход к следующей итерации цикла.

Пример вывода четных чисел:

for ( int num = 0; num < 100; num++ )

{

if ( num % 2 ) continue;

cout << num << "\n";

}
  1. Оператор return

Этот оператор завершает выполнение функции, в которой он задан, и возвращает управление в вызывающую функцию.

Управление передается в вызывающую функцию в точку, непосредственно следующую за вызовом.

Если return присутствует в функции main(), то он вызывает прерывание выполнения программы.

Массивы


Массив – это последовательность однотипных элементов, имеющих единый идентификатор и хранящихся в смежных ячейках памяти.

Единственный оператор для массива – индексация []. Начинается с 0. Индекс – всегда целое выражение.

Объявление массивов:

int massiv[5];

float d[4];

Размер массива при объявлении – всегда целое константное выражение. Нельзя объявить массив

int m[i]

Массив можно инициализировать одним из трех способов:
  • при создании массива — используя инициализацию по умолчанию (этот метод применяется только для глобальных и статических массивов );
  • при создании массива — явно указывая начальные константные значения.
  • в процессе выполнения программы — путем записи данных в массив.

Двумерный массив рассматриваются как массив элементов, каждый из которых является одномерным массивом.

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


Контроль за типом указателя в С более строгий, чем за типом переменной:
  1. &.

px = &x; // взятие адреса
  1. *

Унарная операция * называется операцией разыменования (разадресации, операцией разрешения адреса). Эта операция рассматривает свой операнд как адрес и обращается по этому адресу, чтобы извлечь объект, содержащийся по этому адресу.

z = *px + *py;

*pz = *px + *py;
  1. =

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;


Но нельзя сравнивать:

if (x==y) cout << "*";

Структуры могут быть вложенными – учетная карточка служащего может фактически выглядеть так:

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; // то же самое, но короче и нагдяднее