Как удалить элемент из динамического массива c

от admin

Как удалить элемент из динамического массива c

Кроме отдельных динамических объектов в языке C++ мы можем использовать динамические массивы. Для выделения памяти под динамический массив также используется оператор new , после которого в квадратных скобках указывается, сколько массив будет содержать объектов:

Причем в этом случае оператор new также возвращает указатель на объект типа int — первый элемент в созданном массиве.

В данном случае определяется массив из четырех элементов типа int, но каждый из них имеет неопределенное значение. Однако мы также можем инициализировать массив значениями:

При инициализации массива конкретными значениями следует учитывать, что если значений в фигурных скобках больше чем длина массива, то оператор new потерпит неудачу и не сможет создать массив. Если переданных значений, наоборот, меньше, то элементы, для которых не предоставлены значения, инициализируются значением по умолчанию.

Стоит отметить, что в стандарт С++20 добавлена возможность выведения размера массива, поэтому, если применяется стандарт С++20, то можно не указывать длину массива:

После создания динамического массива мы сможем с ним работать по полученному указателю, получать и изменять его элементы:

Причем для доступа к элементам динамического массива можно использовать как синтаксис массивов ( numbers[0] ), так и операцию разыменования ( *numbers )

Соответственно для перебора такого массива можно использовать различные способы:

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

Для удаления динамического массива и освобождения его памяти применяется специальная форма оператора delete :

Чтобы после освобождения памяти указатель не хранил старый адрес, также рекомендуется обнулить его:

Многомерные массивы

Также мы можем создавать многомерные динамические массивы. Рассмотрим на примере двухмерных массивов. Что такое по сути двухмерный массив? Это набор массив массивов. Соответственно, чтобы создать динамический двухмерный массив, нам надо создать общий динамический массив указателей, а затем его элементы — вложенные динамические массивы. В общем случае это выглядит так:

Вначале выделяем память для массива указателей (условно таблицы):

Затем в цикле выделяем память для каждого отдельного массива (условно строки таблицы):

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

6.Динамически выделяемая память, динамические массивы (вставка, удаление элементов с концов и в середине).

Как было сказано раньше, массивы также могут быть динамическими. Чаще всего операции new и delete применяются, для создания динамических массивов, а не для создания динамических переменных. Рассмотрим фрагмент кода, создания одномерного динамического массива.

// объявление одномерного динамического массива на 10 элементов:

float *ptrarray = new float [10];

// где ptrarray – указатель на выделенный участок памяти под массив вещественных чисел типа float

// в квадратных скобочках указываем размер массива

После того как динамический массив стал ненужным, нужно освободить участок памяти, который под него выделялся.

// высвобождение памяти отводимой под одномерный динамический массив:

После оператора delete ставятся квадратные скобочки, которые говорят о том, что высвобождается участок памяти, отводимый под одномерный массив. Разработаем программу, в которой создадим одномерный динамический массив, заполненный случайными числами.

// new_delete_array.cpp: определяет точку входа для консольного приложения.

// в заголовочном файле <ctime> содержится прототип функции time()

// в заголовочном файле <iomanip> содержится прототип функции setprecision()

using namespace std;

Int main(int argc, char* argv[])

srand(time(0)); // генерация случайных чисел

float *ptrarray = new float [10]; // создание динамического массива вещественных чисел на десять элементов

for (int count = 0; count < 10; count++)

ptrarray[count] = (rand() % 10 + 1) / float((rand() % 10 + 1)); //заполнение массива случайными числами с масштабированием от 1 до 10

delete [] ptrarray; // высвобождение памяти

Созданный одномерный динамический массив заполняется случайными вещественными числами, полученными c помощью функций генерации случайных чисел, причём числа генерируются в интервале от 1 до 10, интервал задается так — rand() % 10 + 1. Чтобы получить случайные вещественные числа, выполняется операция деления, с использованием явного приведения к вещественному типу знаменателя — float((rand() % 10 + 1)). Чтобы показать только два знака после запятой используем функцию setprecision(2), прототип данной функции находится в заголовочном файле <iomanip>. Функция time(0) засевает генератор случайных чисел временным значением, таким образом, получается, воспроизводить случайность возникновения чисел.

7.Связные списки: виды списков, итерирование, поиск максимума и минимума, поиск индекса элемента по значению, поиск значения по индексу.

8.Связные списки: операции вставки и удаления элементов с концов и в середине, перестановка местами элементов в списке (без копирования).

таблице 1 перечислены операции вставки и удаления элементов в списках. Списки поддерживают все функции деков, а также специальные реализации алгоритмов remove() и remove_if(). Как это обычно бывает при использовании STL, правильность аргументов обеспечивается вызывающей стороной. Итераторы должны ссылаться на правильные позиции, конец интервала не должен предшествовать началу, элементы не должны удаляться из пустого контейнера.

Таблица 1. Операции вставки и удаления для списковОперация Описание

c.insert(pos,elem) -Вставляет копию elem в позицию итератора pos и возвращает позицию нового элемента

c.insert(pos,n,elem) -Вставляет n копий elem в позицию итератора pos (и не возвращает значения)

c.insert(pos,beg,end) -Вставляет копию всех элементов интервала [beg,end) в позицию итератора pos (и не возвращает значения)

c.push_back(elem)- Присоединяет копию elem в конец списка

c.pop_back() -Удаляет последний элемент (не возвращая его)

c.push_front(elem) -Вставляет копию elem в начало списка

c.pop_front() -Удаляет первый элемент (не возвращая его)

c.remove(val) -Удаляет все элементы со значением val

c.remove_if(op) -Удаляет все элементы, для которых op(elem) возвращает true

c.erase(pos) -Удаляет элемент в позиции итератора pos и возвращает позицию следующего элемента

c.erase(beg,end) -Удаляет все элементы из интервала [beg,end) и возвращает позицию следующего элемента

c.resize(num)- Приводит контейнер к размеру num (если size() при этом увеличивается, новые элементы создаются своим конструктором по умолчанию)

c.resize(num,elem)- Приводит контейнер к размеру num (если size() при этом увеличивается, новые элементы создаются как копии elem)

c.clear() -Удаляет все элементы (контейнер остается пустым)

Вставка и удаление выполняются быстрее, если группа элементов обрабатывается одним вызовом вместо нескольких последовательных вызовов.

Для удаления элементов в списках предусмотрены специализированные версии алгоритмов remove(). Эти функции работают быстрее алгоритмов remove(), потому что используют вместо элементов только внутренние указатели. Следовательно, в отличие от векторов или деков операцию remove() для списков следует вызывать в форме функции класса, а не алгоритма (смотри шаг 136). Чтобы удалить все элементы с заданным значением, воспользуйтесь следующей конструкцией (за подробностями обращайтесь на 109 шаг):

// Удаление всех элементов со значением val

Однако для того чтобы удалить только первый экземпляр искомого значения, придется воспользоваться алгоритмом (по аналогии с тем, как показано на шаге 136 для векторов).

Функция remove_if позволяет определить критерий удаления элементов в виде функции или объекта функции. Она удаляет каждый элемент, для которого передаваемая операция возвращает true. Пример использования remove_if() для удаления всех элементов с четными значениями:

: Вставка элемента в середину 1-связного списка

Односвязные и двусвязные списки.

Массивы и записи — простейшие примеры структур данных. С их помощью можно моделировать любые сколь угодно сложные информаионные структуры

C++. Класс vector. Методы, изменяющие данные в массиве

Класс vector. Методы, изменяющие данные в массиве. Методы push_back() , pop_back() , clear() , swap() , operator=() , erase() , insert() , assign()

Перед изучением данной темы рекомендуется ознакомиться со следующей темой:

Содержание

  • 1. Метод push_back() . Добавить элемент в конец вектора
  • 2. Метод pop_back() . Удалить последний элемент вектора
  • 3. Метод clear() . Удаляет из массива все элементы
  • 4. Метод swap() . Обмен местами двух векторов
  • 5. Присваивание массивов. Перегруженный оператор =
  • 6. Метод erase() . Удалить элемент или несколько элементов указанного диапазона
  • 7. Метод insert() . Вставляет элемент или группу элементов в вектор
    • 7.1. Вставка списка инициализации в вектор
    • 7.2. Вставка элемента заданное количество раз в заданную позицию
    • 7.3. Вставка одиночного элемента в заданную позицию
    • 7.4. Вставка нескольких элементов из указанного диапазона

    Поиск на других ресурсах:

    1. Метод push_back() . Добавить элемент в конец вектора

    Чтобы добавить элемент в конец массива используется метод push_back() . Метод имеет 2 перегруженные реализации, синтаксис объявления которых следующий

    здесь T – тип элементов вектора.

    Пример.

    2. Метод pop_back() . Удалить последний элемент вектора

    Для удаления последнего элемента динамического массива используется метод pop_back() . Синтаксис объявления метода следующий

    здесь T – тип элементов массива.

    Пример.

    3. Метод clear() . Удаляет из массива все элементы

    Метод clear() предназначен для удаления из массива всех элементов. Длина массива, которая возвращается методом size() , становится равной 0. Синтаксис объявления метода clear() следующий:

    здесь T – тип элементов массива.

    Пример.

    4. Метод swap() . Обмен местами двух векторов

    Метод swap() используется для обмена двух векторов местами. Типы векторов должны совпадать. Общая форма объявления метода следующая

    • T – тип элементов вектора;
    • _Right – массив, который обменивается с текущим массивом.

    Пример.

    5. Присваивание массивов. Перегруженный оператор =

    Динамический массив типа vector допускает операцию присваивания одного массива другому с помощью перегруженного оператора = . В этом случае создается дополнительная копия другого массива. Таким образом, массивы располагаются в разных участках памяти.

    Приклад.

    6. Метод erase() . Удалить элемент или несколько элементов указанного диапазона

    Метод erase() используется для удаления элементов из динамического массива типа vector . Метод имеет две перегруженные реализации.
    Первая реализация имеет следующий синтаксис:

    • T – тип элементов массива;
    • First – итератор, который указывает на первый элемент удаляемого диапазона;
    • Last – итератор, указывающий на элемент, находящийся за последним элементом удаляемого диапазона.
      Данная реализация метода erase() возвращает итератор, указывающий на фрагмент исходного массива, который начинается с итератора Last .

    Вторая реализация имеет синтаксис:

    • T – тип элементов массива;
    • Where – итератор, указывающий на элемент в массиве, который нужно удалить.

    В этой реализации возвращается итератор, указывающий на начало исходного массива.

    Пример. В примере приведена демонстрационная программа, в которой используется метод erase() .

    Результат выполнения программы

    7. Метод insert() . Вставляет элемент или группу элементов в вектор

    Метод insert() обеспечивает несколько разновидностей вставки одного или нескольких элементов в массив. Каждая разновидность определена отдельной перегруженной реализацией метода.

    7.1. Вставка списка инициализации в вектор

    При этом способе вставки объявление метода insert() следующее:

    • T – тип элементов массива;
    • initializer_list – тип списка инициализации;
    • _Where – итератор, указывающий на позицию вставки;
    • _Ilist – список инициализации.

    Метод возвращает итератор, указывающий на начало фрагмента вставки.

    Пример.

    Результат выполнения программы

    7.2. Вставка элемента заданное количество раз в заданную позицию

    Данная реализация метода позволяет вставить элемент в заданную позицию заданное количество раз

    • T – тип элементов массива;
    • _Where – итератор, указывающий на позицию вставки;
    • _Count – количество элементов Val , которые нужно вставить в массив;
    • Val – элемент, вставляемый в массив.

    Пример.

    Результат выполнения программы

    7.3. Вставка одиночного элемента в заданную позицию

    Для вставки одиночного элемента в позицию, на которую указывает итератор, используется одна из следующих форм метода insert()

    • T – тип элементов массива;
    • _Where – итератор, указывающий на позицию вставки;
    • _Val – вставляемое значение.

    Данная форма функции возвращает итератор, указывающий на позицию вставки.

    Пример.

    Результат выполнения программы

    7.4. Вставка нескольких элементов из указанного диапазона

    Для вставки группы элементов из другого массива, которые заданы диапазоном, используется следующая форма метода insert()

    • T – тип элементов массива;
    • _Where – итератор, который указывает позицию вставки в текущем массиве;
    • _First , _Last – итераторы, указывающие на позиции начала и конца диапазона массива, который есть источником.

    Метод возвращает итератор, указывающий на позицию вставки.

    Пример.

    Результат выполнения программы

    8. Метод assign() . Создать массив из существующих данных

    Метод assign() позволяет создать новый массив из существующего массива или существующих данных. Метод assign() имеет несколько перегруженных реализаций.

    Первая реализация позволяет изменить существующий массив и заполнить его значениями

    • T – тип элементов массива;
    • NewSize – новый размер массива (количество элементов);
    • val – значения, которыми заполняется массив.

    Вторая реализация позволяет сформировать один массив из другого. Синтаксис объявления такой формы метода assign() следующий

    Removing elements from dynamic arrays

    It’s reasonably self-explanatory, remove_element removes a given element of a dynamic array.

    As you can see, each element of test is initialised to an incrementing integer (that is, test[n] == n). However, the program outputs

    . Having removed an element of test, one would expect a call to to test[n] where n >= the removed element would result in what test[n+1] would have been before the removal. So I would expect the output

    . What’s going wrong?

    EDIT: The problem has now been solved. Here’s the fixed code (with crude debug printfs), should anyone else find it useful:

    6 Answers 6

    I see several issues in the posted code, each of which could cause problems:

    returning the new array

    Your function is taking an int* array but then you are trying to swap it with your temp variable at the end prior to returning the new array. This will not work, as you are simply replacing the local copy of int* array which will disappear after you return from the function.

    You either need to pass your array pointer in as an int** , which would allow you to set the actual pointer to the array in the function, or, I would suggest just returning a value of int* for your function, and returning the new array.

    Also, as mentioned in this answer, you really don’t even need to reallocate when deleting an element from the array, since the original array is big enough to hold everything.

    size and offset calculations

    You are using sizeof(int*) for calculating the array element size. This may work for some types, but, for instance, for a short array sizeof(short*) does not work. You don’t want the size of the pointer to the array, you want the size of the elements, which for your example should be sizeof(int) although it may not cause problems in this case.

    Your length calculation for the offsets into the arrays looks ok, but you’re forgetting to multiply the number of elements by the element size for the size parameter of the memcpy. e.g. memcpy(temp, array, indexToRemove * sizeof(int)); .

    Your second call to memcpy is using temp plus the offset as the source array, but it should be array plus the offset.

    Your second call to memcpy is using sizeOfArray — indexToRemove for the number of elements to copy, but you should only copy SizeOfArray — indexToRemove — 1 elements (or (sizeOfArray — indexToRemove — 1) * sizeof(int) bytes

    Wherever you are calculating offsets into the temp and array arrays, you don’t need to multiply by sizeof(int), since pointer arithmetic already takes into account the size of the elements. (I missed this at first, thanks to: this answer.)

    looking at incorrect element

    You are printing test[16] (the 17th element) for testing, but you are removing the 16th element, which would be test[15] .

    corner cases

    Also (thanks to this answer) you should handle the cases where indexToRemove == 0 and indexToRemove == (sizeOfArray — 1) , where you can do the entire removal in one memcpy.

    Also, you need to worry about the case where sizeOfArray == 1 . In that case perhaps either allocate a 0 size block of memory, or return null. In my updated code, I chose to allocate a 0-size block, just to differentiate between an array with 0 elements vs. an unallocated array.

    Returning a 0-size array also means there are no additional changes necessary to the code, because the conditions before each memcpy to handle the first two cases mentioned will prevent either memcpy from taking place.

    And just to mention, there’s no error handling in the code, so there are implicit preconditions that indexToRemove is in bounds, that array is not null, and that array has the size passed as sizeOfArray .

    example updated code

    a few words on memory management/abstract data types

    Finally, something to consider: there are possible issues both with using malloc to return memory to a user that is expected to be free d by the user, and with free ing memory that a user malloc ed. In general, it’s less likely that memory management will be confusing and hard to handle if you design your code units such that memory allocation is handled within a single logical code unit.

    For instance, you might create an abstract data type module that allowed you to create an integer array using a struct that holds a pointer and a length, and then all manipulation of that data goes through functions taking the structure as a first parameter. This also allows you, except within that module, to avoid having to do calculations like elemNumber * sizeof(elemType) . Something like this:

    This is a basically implementing some C++-like functionality in C, and it’s IMO a very good idea, especially if you are starting from scratch and you want to create anything more than a very simple application. I know of some C developers that really don’t like this idiom, but it has worked well for me.

    The nice thing about this way of implementing things is that anything in your code that was using the function to remove an element would not ever be touching the pointer directly. This would allow several different parts of your code to store a pointer to your abstract array structure, and when the pointer to the actual data of the array was reallocated after the element was removed, all variables pointing to your abstract array would be automatically updated.

    In general, memory management can be very confusing, and this is one strategy that can make it less so. Just a thought.

    Читать:
    Сколько натуральных решений имеет уравнение

Похожие статьи