#include <cstring>
// Возвращает длину строки p
size_t strlen(const char* p);
// Копирует строку q в строку p
char* strcpy(char* destination, const char* source);
// Возвращает указатель на последний символ скопированной строки
// Сравнение строк в лексикографическом порядке
int strcmp(const char* s1, const char* s2);
// < 0, s1 < s2
// = 0, s1 == s2
// > 0, s1 > s2
// Добавляет source в конец destination
char* strcat (char* destination, const char* source);
// Считается, что в строке s1 достаточно памяти
// Ищет в строке s символ с
char* strchr(const char* s, char c);
// Ищет в строке s подстроку s1
char* strstr(const char* s, const char* s1);
char* s1 = "Hello";
char s1[]; // Указатель объявляется как неизменяемый
char* s2;
strcpy(s2, s1); // Не работает, потому что память под s2 не выделена.
char s2[4];
strcpy(s2, s1);
Контроль памяти лежит на программисте. В случае возникновения ошибки она, по-началу, может себя никак не проявить.
Обычно такие опасные функции стараются не использовать. Вместо них применяют аналогичные функции, контролирующие размер записываемых данных.
char* s1 = "Hello";
char s2[4];
strncpy(s2, s1, 4); // Защищает от переполнения
В C/С++ динамическая память управляется с помощью указателей.
Замечание. В С++ сборщика мусора нет! Ответственность за выделение и освобождение памяти лежит на программисте.
Если необходимо в динамической памяти выделить место для int
, используется
оператор new
.
int* pi = new int;
*pi = 5;
(*pi)++; // *pi == 6
Освобождение памяти производится с помощью оператора delete
.
delete pi;
pi = nullptr; // Хороший стиль
Присваивание, указателю на высвобожденную память, значения nullprt
защищает
от ошибки при повторном (ошибочном вызове) delete
.
int *pi;
*pi = 5
int *pi = new int;
pi = new int;
// Особенно сильно это чувствуется при использовании в циклах
for(;;)
pi = new int;
Если в процессе работы функции выделяется динамическая память, то ее следует освобождать в теле той же функции. Если же указатель на динамически выделенную память передается в качестве результата работы функции, то необходимо озаботиться об освобождении этой памяти вне тела этой функции. Такой принцип работы не всегда очевиден - тем и опасен.
void f()
{
int* pi = new int;
}
int *pi = new int;
*pi = 5
delete pi;
*pi = 6;
int* pi new int[10]; // Выделение памяти для 10 элементов
pi[0] = 5;
pi[1] = 3;
...
Для возврата этой памяти используется оператор delete[]
:
delete[] pi;
Примечание. Обычно менеджер динамической памяти размещает в отрицательных адресах массива размер выделенной памяти, однако использовать эту информацию, скорее всего, не удастся.
void print(const int* pi, int size)
{
for(int i = 0; i < n, i++)
cout << pi[i] << ' ';
}
...
int* pi = new int[10];
print(pi, 10);
int a [3][4]; // размеры это константы, поскольку память должна выделяться
// на этапе компиляции
void f(int a[][4], int m, int n)
{
...
}
Вообще говоря. так писать плохо. В таких случаях лучше использовать двумерные динамические массивы.
int** a; // a - указатель на начало массива из int*
int m, n;
cin >> m >> n; // Здесь размеры определяются на этапе выполнения программы
a = new int*[m];
Каждый элемент имеет тип int*
и значение NULL
.
for(int i = 0; i < m; i++)
a[i] = new int[n];
// Теперь можно обращаться
a[1][2] ~ *(*(a+1)+2)
void print(const int** a, int m, int n)
{
for(int i = 0; i < m; i++)
{
for(int j = 0; i < n; j++)
cout << a[i][j] << ' ';
cout << endl;
}
}
Двумерный статический массив в эту функцию передать нельзя.
Замечание. Динамические массивы позволяют задавать свой размер во время выполнения программы.
for(int i = 0; i < m; i++)
delete[] a[i];
delete[] a;
struct node
{
int dat;
node* next;
node(int data, node* next)
{
// this указатель на себя
this->data=data;
this->node=next;
}
};
В С++ объект вызова конструктора не содержится в динамической памяти.
node n1(4, nullptr);
Память под n1
выделяется на стеке самой программы, а конструктор лишь инициализирует поля.
node n2(3, &n1);
node n3(5, &n2);
node* p = &n3;
Шаблоны впервые появились в C++, затем они перекочевали в других языках программирования
template<typename T>
struct node
{
T dat;
node<T>* next;
node(T data, node<T>* next)
{
// this указатель на себя
this->data=data;
this->node=next;
}
};
Теперь предыдущий пример будет выглядеть следующим образом:
node<int> n1(4, nullptr);
node<int> n2(3, &n1);
node<int> n3(5, &n2);
node<int>* p = &n3;