Динамическое выделение памяти в C++

Язык C++ / Динамическое выделение памяти в C++

 

В Си работать с динамической памятью можно при помощи соответствующих функций распределения памяти (calloc, malloc, free), для чего необходимо подключить библиотеку
malloc.h

С++ использует новые методы работы с динамической памятью при помощи операторов new и delete:

  • new — для выделения памяти;
  • delete — для освобождения памяти.

Оператор new используется в следующих формах:

  • new тип; — для переменных
  • new тип[размер]; — для массивов

Память может быть распределена для одного объекта или для массива любого типа, в том числе типа, определенного пользователем. Результатом выполнения операции new будет указатель на отведенную память, или исключение std::bad_alloc в случае ошибки.

 
 
 
 
 
 
 
int *ptr_i;
double *ptr_d;
struct person *human;

ptr_i = new int;
ptr_d = new double[10];
human = new struct person;

Память, отведенная в результате выполнения new, будет считаться распределенной до тех пор, пока не будет выполнена операция delete.

Освобождение памяти связано с тем, как выделялась память – для одного элемента или для нескольких. В соответствии с этим существует и две формы применения delete:

  • delete указатель; — для одного элемента
  • delete[] указатель; — для массивов

Например, для приведенного выше случая, освободить память необходимо следующим образом:

 
 
 
delete ptr_i;
delete[] ptr_d;
delete human;


Освобождаться с помощью delete может только память, выделенная оператором new.
Пример Создание динамического массива
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
int main()
{
  int size;
  int *dan;
  system("chcp 1251");
  system("cls");
  cout << "Ввести размерность массива : ";
  cin >> size;
  dan = new int[size];
  for (int i = 0; i<size; i++) {
    cout << "dan[" << i << "] = ";
    cin >> dan[i];
  }
  for (int i = 0; i<size; i++)
    cout << dan[i] << " ";
  delete[] dan;
  cin.get(); cin.get();
  return 0;
}

Результат выполнения
Операторы new и delete

Указатель dan – базовый адрес динамически распределяемого массива, число элементов которого равно size. Операцией delete освобождается память, распределенная при помощи new.

Пример неудачного выделения памяти (в случае очень большого требуемого объема):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
int main()
{
    int *p = 0;
    try
    {
         p = new int[1000000000];
    }
    catch(const bad_alloc& e)
    {
        cout << "Error: " << e.what() << '\n';
    }
    cout << p << endl;
    return 0;
}


Назад: Язык C++

Комментариев к записи: 30

  • Зачем вы вводите людей в заблуждение? Сами не разбираетесь в вопросе и ещё чему-то других учите. Оператор new при обычном использовании __никогда__ не возвращает нулевой указатель, чтобы заставить оператор new вернуть NULL требуется другой синтаксис вызова, но это явно не ваш случай. Учите матчасть и морочьте людям голову.

  • Можно ли изменить размер выделенной памяти по new? Для malloc есть realloc, а для new ничего подобного нет :-(



      • Дмитрий
        Эм, да очень просто! Попробуйте выделить памяти у системы на гигов 5. И система вернёт не указатель на участок памяти, а NULL. На равенство указателя этому NULL и можно определить, выделилась память или нет.

        • Не верно. Запроси ты у системы хоть 20 Гб, она не выделит (см.виртуальная память, система подкачки памяти). Но даже если системы не сможет выделить память для процесса, она бросит badalloc. Система не может вернуть нуль-указатель. Если было бы так, то это спровоцировало бы undefined behaviour.

    • 1
      2
      3
      4
      int variable  = new (std::nothrow) int variable (5);
      if (!variable) { //если не сможет выделить, вернет NULL
           std::cout << "Could not allocate memory for variable"
      }

  • Николай
    Елена, скажите, а если динамическая память выделяется в функции, очищать её где надо там же в функции или в int main()?

    • Елена Вставская
      Зависит от того, будете ли Вы ее в main использовать. Например, если Вам в функции нужно сформировать массив, и указатель на него вернуть в main(), чтоб потом с ним работать, очевидно очищать память нужно в main.
      А если это вспомогательный массив был, который после выхода из функции больше не нужен, то память нужно очистить в функции.

  • По моему - рекурсия в данном случае, это воспаленное сознание вашего преподавателя:
    1
    2
    3
    4
    5
    double g = 1.000001;    
    for (uint32_t i=0; i<(1000000-1); i++)
    {
    g *= 1.000001;
    }
    Все решается, может неоптимально по скорости, зато неоправданно память не загаживаем. А то современные программы привыкли оперативку жрать тоннами )))

  • Почему то не могу ответить на ваш пост, поэтому приходтся создать новую ветку. условия задачи:
    Возводить в степень можно гораздо быстрее, чем за n умножений! Для этого нужно воспользоваться следующими рекуррентными соотношениями: an = (a2)n/2  при четном n, an = a × an−1 при нечетном n. Реализуйте алгоритм быстрого возведения в степень с помощью рекурсивной функции. Формат входных данных Вводятся действительное число a и целое неотрицательное число n. Формат выходных данных
    Выведите ответ на задачу. Но следующая задача которую надо решить тоже с помощью рекурсии это найти все возможные расстановки ферзей.
    Дано число N. Определите, сколькими способами можно расставить на доске N×N N ферзей, не бьющих друг друга.
    Формат входных данных
    Задано единственное число N. (N ≤ 10)
    /////////////////////////////// Но если сосчитать все возможные варианты расстановки ферзей на поле это 8 в степени 64? или 8 в степени 8 Но даже если это просто 8^8 то это будет 16.777.216!!! Задачку с рекурсиями дает Yandex а с длинными строчками Mail.ru Выглядит так что уметь решать их для них это очень важно.  

  • Вот у меня стоит задача создать функцию, которая будет динамически увеличивать размер строки. Но вот засада, данный код принимает размер строки длинною только в 4094 символа. Почему так происходит? И как это можно обойти?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <iostream>
    #include <cstring>
    char *resize(const char *str, unsigned size, unsigned new_size);
    char *getline();
    using namespace std;

    int main()
    {
    char *s;
    s= getline();
    cout<<s<<endl;
    }
    // Принимает строку, размер этой строки, новый размер строки, возвращает указатель на новую строку.
    char *resize(const char *str, unsigned size, unsigned new_size)
    {
    char *ns = new char [sizeof(char)*new_size];
    for (unsigned i=0;i<new_size&&i<size;i++)
    {
    ns[i]=str[i];
    }
    delete []str;
    return ns;
    }
    // Получаем строку и увеличиваем ее понеобходимости.
    char *getline() //
    {
    unsigned j=1;    //размер массива
    char *temp= new char [j]{'\0'};
    char c;
    while(std::cin.get(c) && c != '\n' && c != std::cin.eof())
    {
    temp[j-1]=c;
    temp=resize (temp,j,++j);
    }
    temp[j-1]='\0';
    return temp;
    }

    • Елена Вставская
      Честно говоря, не сталкивалась с подобной проблемой, но она действительно имеет место. Может быть, можно ввести данные в несколько строк?

      • К сожалению это невозможно. Необходимо обработать строку длиною в тысяч сто символов. PS прохожу курс на Stepic.org Там никто не отвечает. На форумах сидят такие же как и я, ничего не знают. Поэтому решил тут спросить)
        В параллельном курсе stepic необходимо создать рекурсию которая будет способна возвести 1.000001 в степень 1000000 Но при такой глубине у меня рекурсия не работает. Происходит переполнение стека.
        Как же работать с памятью то?

        • Елена Вставская
          А вот с этой задачей, видимо предполагается использование динамического программирования. 1,000001 * 1,000001 = 1,000002000001 1,000002000001 * 1,000001 = 1,000003000003000001 и т.д. То есть надо выявить закономерность, как изменяется число при умножении его на 1,000001 и в соответствии с этим сформировать результат. P.S. В результате получится число e.

          • Так рекурсивная функция должна правильно обработать все возможные возведения в степень, не только 1.000001 в степени 1000000 да и на третем шаге число 1,000003000003000001 уже выйдет за пределы всего что только можно. a^n = (a^2)^(n/2)  при четном n, a^n = a × a^(n−1) при нечетном n. Рекурсию то я создал. Но она не может уйти в глубину на 1.000.000 Максимум можно уйти в глубь около 86.674 раза. А по условию задачи необходимо что бы рекурсия была способна войти вглубь на 1.000.000.
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            ////////////////////////////////////////////////////////////////////////////////////////////////
            #include <iostream>
            using namespace std;
            double m(double a, unsigned int n);
            double mm(double a, double n);
            int main()
            {
                double a=1.000001;
                unsigned int b=10000; // максимально возможная степень:86674
                if (b%2==0)cout<< mm (a,b);
                if (b%2!=0)cout<< m(a,b);
            }
            double m(double a, unsigned int n)
            {
                if (n==0)return 1;
                return a*m(a,n-1);
            }
            double mm(double a,  double n)
            {
                if (n==0)return 1;
                return (m(m(a,2),(n/2)));
            }
            ///////////////////////////////////////////////////////////////////
            PS в 50-60% случаях при вооде защитного кода графическая надпись не обновляется, поэтому происходит неверный ввод кода. И после неверного ввода весь текст что был введен пропадает, очень обидно. То есть можно зайти на одну и туже страницу 10 раз и всегда будет одно и тоже графическое число например: "Шесть тысяч восемьсот восемьдесят два". Но данный код неверный. Перезагрузка страницы F5 не помогает.

          • Елена Вставская
            А как предлагает решать задачу тот, кто условие придумал? P.S. Отключила на время кэширование сайта, чтобы избежать проблемы с капчей. Посмотрим, будет лучше или хуже.

    • Знаю что не актуально, но может кто будет это читать
      temp=resize (temp,j,++j); - здесь ошибка.
      j - в данном случае будет сначала инкрементироваться, а затем отправляться. в итоге будет нечно вроде такого j = 1
      resize (temp,j,++j) - вид вызова будет такой resize (temp,2,2)
      для решения необходимо вызывать так
      resize (temp,j - 1,++j)
      или так
      resize (temp,j,j++ + 1)
      или в две строки
      resize (temp,j,j + 1)
      j++

  • я написал программу как мне устанавливать на другие компьютеры и как выложить в интернет

    • Елена Вставская
      Проще всего выложить вконтакте или на github.com. Но можно создать свой сайт и заниматься его оформлением и наполнением. А вообще пока у меня до обучения размещению информации в Интернете дело не дошло :)

  • А реально ли создать или написать чтобы выложить в интернет чтобы все скачивали и пользовались что я создам на языке c++?

  • Увы нет Елена. Ранее я уже писал на форуме в теме http://prog-cpp.ru/c-alloc/. Решил использовать этот же алгоритм:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    int main() {
      do {
        system("CLS");
        //creating the dynamic array[0][0] with only one free space
        float **a = (float**)malloc(sizeof(float*));
        a[0] = (float*)malloc(sizeof(float));
        int volume = 0, i = 0;//volume contains used space(bytes) in array, ′i′ need for counting below
        do {
          cout << "Enter " << i + 1 << " string: ";
          do {
            cin >> a[i][_msize(a[i]) / sizeof(float) — 1];//the ′j′ is memory of *p divided by 4, for first one 4/4 = 1, next 8/4 and etc
            if (cin.rdbuf()->in_avail() != 1) a[i] = (float*)realloc(a[i], _msize(a[i]) + sizeof(float));//realocates memory only if buffer contains more then 1 number in stack
          } while (cin.get() != EOS);//while not in the end of buffer >′cin′
          volume += _msize(a[i]);//increases volume 4/8/16 and etc
          a = (float**)realloc(a, _msize(a) * sizeof(float*));//reallocates new pointer
          a[i + 1] = (float*)malloc(sizeof(float));//allocates memory for new p[]
          i++;
        } while (_getch() != ESC);//while you dont press ESC
        cout << endl << "Result achived:" << endl << endl;
        volume /= _msize(a[0]);//strings in array calc
                     //output on screen
        for (int i = 0; i < volume; i++) {
          for (int j = _msize(a[i]) / sizeof(float); j > 0; j—) {//′j′ is a amount of numbers in string, 1 1 1 1 all size is 16, divided by 4 = 4
            cout << a[i][_msize(a[i]) / sizeof(float) — j] << " ";
          }
          cout << endl;
          free(a[i]);//frees memory in *p
        }
        free(a);//frees memory in **p
      } while (_getch() != ESC);//while you dont press ESC
      return 0;
    }
    Вот тут в прототипе все работает корректно. Однако при попытке создании структуры что-то пошло не так.

    • Елена Вставская
      А где в Вашей реализации строки
      1
      2
      float **a = (float**)malloc(sizeof(float*));
      a[0] = (float*)malloc(sizeof(float));
      ??? Это и есть изначальное выделение памяти - то, о чем я говорила.

      • То есть, вы хотите сказать что:
        1
        2
        3
        4
        5
        typedef struct {    
        float **matrix;
        }matrix;
        A.matrix = (float**)malloc(sizeof(float));
        A.matrix[0] = (float*)malloc(sizeof(float));
        Не эквивалентно:
        1
        2
        float **a = (float**)malloc(sizeof(float*));
        a[0] = (float*)malloc(sizeof(float));

        • Елена Вставская
          Борис, 1) В функции input_matrix(matrix m) Вы работаете с копией матрицы, а не с ее оригиналом, поэтому после возврата из функции введенные данные недоступны. Один из способов решения этой проблемы:
          1
          2
          3
          4
          matrix input_matrix(matrix m) {

          return m;
          }
          2) Я не поняла назначение цикла do...while в main(), но у меня он работать отказывается. 3) Очистка памяти в конце функции main() производится неверно. Один из вариантов очистки памяти
          1
          2
          3
          4
          5
          int h = A.volume / _msize(A.matrix[0]);
          // это вычисление обязательно сделать до цикла, поскольку количество строк в цикле будет меняться
          for(int i=0; i<h; i++)
          free(A.matrix[i]);
          free(A.matrix);

  • Доброго времени суток! Столкнулся с ситуацией, когда выгодным решением стало затолкать динамик в структуру. После компиляции, которая прошла успешно кстати, при работе с программой не могу взаимодействовать с элементами динамика. Есть мнение, что неправильно обращаюсь к адресу хранения. Подскажите пожалуйста, как правильно нужно это делать? Привожу пример кода:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    #include <iostream>
    #include <conio.h>
    #define ESC 27
    #define EOS '\n'
    using namespace std;

    typedef struct {
      float **matrix;
      unsigned int volume;
      char name;
    }matrix;

    //функция ввода.
    void input_matrix(matrix m) {
      //если значения вводились ранее, удалить текущую матрицу
      if (m.volume != 0) {
        m.volume /= _msize(m.matrix[0]);
        for (int i = 0; i < m.volume; i++) {
          free(m.matrix[i]);
        }
        free(m.matrix);
        m.volume = 0;
      }

      int i = 0;

      do {
        cout << "Enter " << i + 1 << " string of matrix " << m.name << ": ";
        do {
          cin >> m.matrix[i][_msize(m.matrix[i]) / sizeof(float) - 1];  //текущий размер массива / размер типа = кол-во элементов в массиве. -1 это свободная ячейка
          if (cin.rdbuf()->in_avail() != 1) m.matrix[i] =
            (float*)realloc(m.matrix[i], _msize(m.matrix[i]) + sizeof(float));  // если в очереди буфера больше 1 символа, записать и увеличить массив m[]
        } while (cin.get() != EOS);   //считать все из буфера cin, то есть до ‘\n’
        m.volume += _msize(m.matrix[i]);                      //суммирую кол-во затрачиваемой памяти
        m.matrix = (float**)realloc(m.matrix, _msize(m.matrix) * sizeof(float*));   //увеличить массив m
        m.matrix[i + 1] = (float*)malloc(sizeof(float));       //выделить память под новые элементы m[]
        i++;
      } while (_getch() != ESC);  //пока не нажал ESC повторять, увеличивая строки массива
    }
    //функция вывода на консоль
    void out_matrix(matrix m) {
      cout << "Matrix " << m.name << ":" << endl;  //вывод имени матрицы
      for (int i = 0; i < m.volume / _msize(m.matrix[0]); i++) {                  // подсчет строк путем деления общего объема на длину строки
        for (int j = _msize(m.matrix[i]) / sizeof(float); j > 0; j--) {         //подсчет элементов путем деления длины строки на тип памяти(float = 4)
          cout << m.matrix[i][_msize(m.matrix[i]) / sizeof(float) -- j] << " "//вывод элементов
        }
        cout << endl;
      }
      cout << endl;
    }

    int main() {
      //для примера я создам объект структуры с названием А
      matrix A;
      A.matrix = (float**)malloc(sizeof(float));
      A.matrix[0] = (float*)malloc(sizeof(float)); //выделяю 4 байта, 1 ячейка
      A.name = 'A';   //имя задам константно
      A.volume = 0;   //начальный объем

      do {
        input_matrix(A);
        out_matrix(A);
        cout << "Volume: " << A.volume << endl;
      } while (_getch() != ESC);

      for (int i = 0; i < A.volume; i++)
        free(A.matrix[i]);
      free(A.matrix);
      return 0;
    }

    • Елена Вставская
      Я, конечно, не уверена на 100%, но по-моему ошибка состоит в том, что когда Вы вводите элементы первой строки матрицы, указатель на эту строку еще не определен - операция
      m.matrix = (float**)realloc(m.matrix, _msize(m.matrix) * sizeof(float*)); //увеличить массив m
      делается позже, после ввода элементов первой строки. В результате первый вызов этой функции создает новую область памяти, игнорируя уже введенные данные для первой строки.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *