- •1. Понятие информации и алгоритма. Формы представления алгоритма.
- •2. Блок-схемы разветвляющихся алгоритмов.
- •3. Блок-схемы циклических алгоритмов.
- •5. Определение переменных. Переменные и константы.
- •6. Операции присваивания. Особенности выполнения.
- •8. Потоковый и форматированный ввод-вывод.
- •9. Использование манипуляторов при вводе и выводе данных.
- •10. Строковый и символьный ввод-вывод информации.
- •12. Особенности синтаксиса и выполнения операторов перехода continue, break.
- •13. Оператор перехода goto. Оператор return.
- •17. Пространство имен. Ключевое слово using как директива.
- •18. Средства отладки программ в ms vs.
- •19. Выполнение приложения с использованием средств интерактивной отладки.
- •20. Выполнение приложения с использованием средств планируемой отладки.
- •21. Создание исполняемого файла.
- •24. Алгоритм метода дихотомии для решения уравнений.
- •27. Побитовые логические операции (конъюнкция, дизъюнкция, сдвиги, инвертирование).
- •28. Символы, строки (объявление, инициализация).
- •Void main()
- •Void main()
- •29. Алгоритмы работы со строками (подсчет дины строки, объединение строк, удаление заданного символа).
- •30. Стандартные функции работы со строками.
- •31. Логические переменные и примеры их использования в программах.
- •32. Понятие указателя.
- •33. Операции над указателями.
- •34. Массивы и указатели. Понятие индекса. Инициализация. Доступ к компонентам.
- •35. Указатели и строки. Примеры работы (объединение строк, поиск заданного символа).
- •36. Матрицы. Инициализация, ввод, вывод.
- •Void main()
- •Int b[nstr][nstb];
- •37. Связь между указателями и элементами матриц. Алгоритмы поиска минимума и суммы элементов матрицы (через указатели).
- •38. Указатели на указатели.
- •39. Динамические переменные. Функции и операторы работы с динамическими переменными. Основ-ные свойства.
- •40. Динамические массивы. Понятие статического и динамического объекта, массива.
39. Динамические переменные. Функции и операторы работы с динамическими переменными. Основ-ные свойства.
40. Динамические массивы. Понятие статического и динамического объекта, массива.
Динамическое выделение памяти необходимо для эффективного использования памяти компьютера. Например, мы написали какую-то программку, которая обрабатывает массив. При написании данной программы необходимо было объявить массив, то есть задать ему фиксированный размер (к примеру, от 0 до 100 элементов). Тогда данная программа будет не универсальной, ведь может обрабатывать массив размером не более 100 элементов. А если нам понадобятся всего 20 элементов, но в памяти выделится место под 100 элементов, ведь объявление массива было статическим, а такое использование памяти крайне не эффективно.
В С++ операции new и delete предназначены для динамического распределения памяти компьютера. Операция new выделяет память из области свободной памяти, а операция delete высвобождает выделенную память. Выделяемая память, после её использования должна высвобождаться, поэтому операции new и delete используются парами. Даже если не высвобождать память явно, то она освободится ресурсами ОС по завершению работы программы. Рекомендую все-таки не забывать про операцию delete.
|
1 2 3 4 |
// пример использования операции new int *ptrvalue = new int; //где ptrvalue – указатель на выделенный участок памяти типа int //new – операция выделения свободной памяти под создаваемый объект. |
Операция new создает объект заданного типа, выделяет ему память и возвращает указатель правильного типа на данный участок памяти. Если память невозможно выделить, например, в случае отсутствия свободных участков, то возвращается нулевой указатель, то есть указатель вернет значение 0. Выделение памяти возможно под любой тип данных: int, float,double, char и т. д.
|
1 2 3 4 |
// пример использования операции delete: delete ptrvalue; // где ptrvalue – указатель на выделенный участок памяти типа int // delete – операция высвобождения памяти |
Разработаем программу, в которой будет создаваться динамическая переменная.
|
1 2 3 4 5 6
|
// new_delete.cpp: определяет точку входа для консольного приложения.
#include "stdafx.h" #include <iostream> using namespace std;
|
Указатели на указатели. Примеры использования.
#include <stdio.h>
int main(void)
{
int x, *p, **q;
x = 10;
p = &x;
q = &p;
printf ("%d", **q) ; /* вывод значения x */
return 0;
}
Функции работы с динамическими массивами на языке С.
malloc void *malloc(unsigned s) - Выделить область памяти размером в s байт и возвратить адрес, при неудачном завершении возвращается NULL
free void *free(void p) - Освободить ранее выделенный с адреса p участок динамической памяти
calloc void * calloc(unsigned n, unsigned m) - Возвратить указатель на начало области динам. памяти для размещения n элементов длиной по m байт каждый, при неудачном завершении возвращается NULL
realloc void * realloc(void * p, unsigned s) - Изменить размер блока ранее выделенной динам. памяти до размера s байт, р - адрес начала изменяемого блока, при неудачном завершении возвращается NULL
Операторы работы с динамическими массивами на языке С++.
Для динамического распределения памяти используются операции new и delete.
int *i;
i = new int(10); //создать динам. перем. типа int, равную 10
float *f;
f = new float; //создать динамическую переменную типа float
int *А = new int[5]; //создать динамич. массив А из 5 элементов
delete i; //освободить память по указателю i
delete f; //освободить память по указателю f
delete [] А; //освободить память, выдел. под массив А
Объявление и определение функции.
Если требуется вызвать функцию до ее определения в рассматриваемом файле, или определение функции находится в другом исходном файле, то вызов функции следует предварять объявлением этой функции.
[тип] имя_функции([формальные_параметры]);
В конце прототипа записывается точка с запятой, а тело функции отсутствует.
Определения функции:
void main() – функция не возвращает значения
int main() – функция возвращает целое значение
float main() – функция возвращает вещ. значение
char main() – функция возвращает симв. значение
bool main() – функция возвращает логич. Значение
Вызов и использование функций.
адресное-выражение ([список-выражений])
Список-выражений представляет собой список фактических параметров, передаваемых в функцию. Этот список может быть и пустым, но наличие круглых скобок обязательно.
void ff()
{ int i = 10;
cout<<"Функция - i="<<i<<endl;
}
void main()
{ int i = 1;
ff();
cout<<"i="<<i<<endl;
}
Способы передачи аргументов в функции. Использование указателей.
Первый способ - передача по значению. Метод копирует содержимое аргумента в формальный параметр подпрограммы. Изменения, сделанные в параметре, не влияют на значение переменной, используемой при вызове.
#include <stdio.h>
int sqr (int x);
int main(void)
{
int t=10;
printf("%d %d", sqr(t), t);
return 0;
}
int sqr (int x) {
x = x*x; return x;
}
В данном примере значение аргумента, передаваемого в sqr(), 10, копируется в параметр х. Когда происходит присваивание х = х * х, модифицируется только локальная переменная х. Переменная t, используемая при вызове sqr(), по-прежнему содержит значение 10. Следовательно, на экране появится «100 10».
Второй способ – передача по ссылке. В данном методе копируется адрес аргумента. В подпрограмме адрес используется для доступа к настоящему аргументу, используемому при вызове. То есть, изменения, сделанные в параметре, влияют на содержимое переменной, используемой при вызове.
#include <stdio.h>
void swap (int *x, int *y);
int main(void)
{
int x, y;
x = 10;
у = 20;
swap(&x, &y);
printf ("%d %d", x, y);
return 0;
}
В данном примере переменной х присваивается значение 10, а у значение 20. Затем вызывается swap() с адресами х и у. Унарный оператор & используется для получения адресов переменных. Поэтому в функцию swap() передаются адреса х и у, а не их значения.
Массивы и строки как параметры функций.
Как правило, массив в параметре функции интерпретируется как указатель.
В программе функция f1 вычисляет длину строки, заданной фактическим параметром buf, формальный параметр - указатель:
#include <stdio.h>
#include <string.h>
int f1 (char *s) {
printf ("\nsizeof(s)=%d",sizeof(s));
for (int l=0;*s;s++,l++);
return l;
}
void main () {
char buf[128];
strcpy (buf,"Hello,win");
printf ("\nresult=%d",f1(buf));
}
Перегрузка функций. Задание параметров функции по умолчанию.
В С++ возможно определение нескольких функций с одинаковым именем, но с разными типами и/или количеством формальных параметров. При этом транслятор выбирает соответствующую функцию по типу аргументов.
//прототипы
int max (int arr[ ], int size);
long max (long arr[ ], int size);
double max (double arr[ ], int size);
double max (long arr[ ], int size); //ошибка
Разные типы возвращаемых значений не дают возможности различать функции. Поэтому компилятор не может различить функции
long max (long arr[ ], int size);
double max (long arr[ ], int size);
Назначение перегрузки – разрешить выполнять одно и то же действие с разными операндами.
Пусть объявлена функция: char* func(long, int = 0);
Инициализатор второго параметра является параметром по умолчанию.
То есть, если в вызове дан только один параметр, в качестве второго используется параметр по умолчанию.
cout << func(31) << func(32, 3);
интерпретируется как
cout << func(31, 0) << func(32, 3);
Задавать параметр по умолчанию можно только для последних параметров.
Аргументы по умолчанию должны быть указаны при первом упоминании имени функции – обычно в прототипе. Значения по умолчанию могут быть константами, глобальными переменными или вызовами функций.
Функции с переменным числом аргументов.
Пример функции с переменным числом параметров, в которой вычисляется сумма значений. В программе список параметров состоит из одного параметра, который является количеством дополнительных параметров:
int sum (int n, ...)
{ int *p = &n;
int s = 0;
for (int i = 1; i <= n; i++)
s += *(++p);
return s;
}
void main()
{ cout << sum(6, 4, 5, 1, 2, 3, 0); }
Указатели как формальные параметры и как результат функции.
Формальные параметры функции представляют собой локальные переменные, которым в момент вызова присваиваются значения фактических параметров.
Формальные параметры внутри функции могут как угодно изменяться - это не затрагивает соответствующих фактических параметров в основной программе.
Если фактический параметр должен быть изменен в подпрограмме-функции, то формальный параметр надо определить как указатель. Тогда фактический параметр должен быть передан с использованием операции &. Пример, в котором происходит обмен значениями переменных:
void swap(int *, int *); // прототип
int main()
{ int a = 10; int b = 20;
cout << a<<' '<< b<<endl;
swap(&a, &b);
cout << a<<' '<< b<<endl;
}
void swap(int *x, int *y)
{ int c;
c = *x; *x = *y; *y = c;
}
Указатель-результат функции может ссылаться не только на отдельную переменную, но и на массив.
Пример. В массиве А увеличить все числа на 1 (внутри функции).
int *ftf(int *pA, int n)
{ for (int i = 0; i < n; i++)
*(pA + i) = *(pA + i) + 1;
return pA;
}
void main()
{ int A[] = {8, 4, 3, 2, 11};
*ftf(A, 5);
for (int i = 0; i < 5; i++)
cout<<A[i]<<' ';
}
Ссылки как формальные параметры и как результат функции.
Ранее рассматривались ситуации, когда при вызове функции значение фактического параметра копировалось в локальную переменную, доступную как формальный параметр внутри функции. Подобный способ передачи параметров по значению имеет следующие ограничения:
1. Из тела функции нельзя обратиться к какому-либо объекту, если он не является глобальным по отношению к функции
2. При передаче больших объектов происходит их копирование и часто память расходуется напрасно.
Для снятия таких ограничений существует возможность передачи параметра по ссылке. Тогда в функцию передаётся адрес объекта и, соответственно, работа внутри функции происходит не с копией, а с оригиналом объекта. Чтобы параметр передавался по ссылке, достаточно в прототипе и в определении функции поставить знак & после типа параметра.
void func1(int val, int& ref)
{ val++; // значение будет увеличено внутри функции, как локальное
ref++; // будет увеличено значение внешней переменной (не создаётся никаких объектов, кроме хранилища адреса ref)
}
void main()
{ int a = 10, b = 10;
func1(a, b);
cout << a << endl; // =10
cout << b << endl; // =11
}
При передаче параметра функции в виде ссылки передаваемый параметр выступает псевдонимом передаваемого фактического параметра. Это исключает копирование и обеспечивает функции прямой доступ к аргументу. Тогда нет необходимости в разыменовании указателей.
int fn (int &n)
{ n++;
return n;
}
void main()
{ int s = 5;
cout << fn(s); //=6
}
Здесь, если в качестве аргумента передать число (например, fn(10)), то компилятор выдаст сообщение об ошибке («невозможно преобразовать параметр из int в int &»). Число 10 – это константа, а изменять значения констант нельзя. Ссылка как параметр функции будет создаваться и инициализироваться при каждом вызове функции и разрушаться по ее завершении, т.е. каждый раз получается новая ссылка.
Пример программы обмена значениями
void change(int &a, int &b)
{ int temp = a;
a = b;
b = temp;
}
void main()
{ int a = 10, b = 100;
change (a, b);
cout << a<<' '<<b; //a=100, b=10
} //Нет необходимости в разыменовании указателей.
Функция может возвращать ссылку. Так как ссылка - это псевдоним, то важно, что объект, на который она ссылается, существует после завершения выполнения функции. Пример:
int& preinc(int& x)
{ ++x;
return x;
}
void main(void)
{ int y = 7, z;
z = preinc(y);
cout << z << endl; // y = 8
preinc(y) = 5; // то же, что ++y
cout << y; // y = 5
}
Пример. Определить минимальный элемент массива А и заменить его на другое значение (на число 1).
double& dmin (double A[], int n)
{ int i, j = 0;
for (i = 1; i < n; i++)
if(A[j] > A[i])
j = i;
return A[j]; //возвр. ссылка на минимум
}
void main( )
{ double s;
double A[ ] = {5, 4.1, 3, 0.2, 11};
s = dmin(A, 5);
cout << s;
dmin(A, 5) = 1.0; // вместо минимума - число 1.0
for ( int i = 0; i < 5; i++)
cout << ' ' <<A[i];
}
Тип возврата объявлен как ссылка, и поэтому возвращается не само значение А[j], а ссылка на него. Будет ошибкой, если указать в качестве возвращаемого значения &А[j]. Это будет означать адрес А[j] элемента, то есть указатель. В этой программе можно определить количество элементов массива через sizeof
double A[ ] = {5, 4.1, 3, 0.2, 11};
int n = sizeof A / sizeof (double);
s = dmin(A, n);
Массивы указателей. Способы формирования.
Массивы указателей допускают различные способы формирования, которые отличаются как способом создания самих элементов, так и способом установления связей между ними.
1. Формирование структуры данных при трансляции: переменные определяются статически, а указатели инициализируются.
double a1, a2, a3, *pd[ ] =
{&a1, &a2, &a3, NULL};
2. Переменные определяются статически, указатели устанавливаются программно.
int i;
double d[19], *pd[20];
for ( i = 0; i < 19; i++)
pd[i] = &d[i];
pd[i] = NULL;
3. Указуемые переменные создаются динамически, массив указателей - статически:
double *p, *pd[20];
for ( i = 0; i < 19; i++)
{ p = new double;
*p = i; pd[i] = p;
}
pd[i] = NULL;
4. Все переменные, в том числе и массив указателей, создаются динамически.
double **pd, *p;
pd = new double *[20];
for ( i = 0; i < 19; i++)
{ p = new double;
*p = i;
pd[i] = p;
}
pd[i] = NULL;
Многоуровневые указатели.
double **pp;
Эту запись можно понимать по-разному:
- это переменная, при обращении к которой получается указатель на переменную типа double;
- это указатель на указатель;
- это адрес указателя.
Существует 4 варианта структур данных
- указатель на одиночный указатель на переменную;

- указатель на одиночный указатель на массив переменных;

- указатель на массив, содержащий указатели на одиночные переменные

- указатель на массив, содержащий указатели на массивы переменных.

