У меня есть программа, которая читает «сырой» список внутриигровых сущностей, и я намереваюсь создать массив, содержащий индексный номер (int) неопределенного количества сущностей, для обработки различных вещей. Я бы не хотел использовать слишком много памяти или ЦП для хранения таких индексов ...
Быстрое и грязное решение, которое я использую до сих пор, - объявить в основной функции обработки (локальный фокус) массив с размером максимального игрового объекта и еще одно целое число, чтобы отслеживать, сколько из них было добавлено в список. Это неудовлетворительно, так как каждый список содержит 3000+ массивов, что не так уж и много, но кажется пустой тратой, поскольку я могу использовать решение для 6-7 списков для различных функций.
Я не нашел конкретных решений для C (не C ++ или C #) для достижения этой цели. Я могу использовать указатели, но я немного боюсь их использовать (если только это не единственный возможный способ).
Массивы не покидают область действия локальной функции (они должны быть переданы функции, а затем удалены) в случае, если это что-то изменит.
Если указатели - единственное решение, как я могу отслеживать их, чтобы избежать утечек?
источник
Ответы:
Если вам нужен динамический массив, вы не можете избежать указателей. А чего ты боишься? Они не будут кусаться (если вы будете осторожны). В C нет встроенного динамического массива, вам просто придется написать его самостоятельно. В C ++ вы можете использовать встроенный
std::vector
класс. C # и почти любой другой язык высокого уровня также имеет аналогичный класс, который управляет динамическими массивами за вас.Если вы планируете написать свой собственный, вот кое-что, что поможет вам начать: большинство реализаций динамических массивов работают, начиная с массива некоторого (небольшого) размера по умолчанию, а затем, когда у вас заканчивается место при добавлении нового элемента, удваивайте размер массива. Как видно из приведенного ниже примера, это совсем не сложно: (для краткости я пропустил проверки безопасности)
Использовать его так же просто:
источник
removeArray
Метод , который позволяет избавиться от последнего элемента также будет аккуратным. Если вы позволите, я добавлю это в ваш образец кода.Как и все, что сначала кажется более пугающим, чем было позже, лучший способ преодолеть первоначальный страх - погрузиться в дискомфорт неизвестного ! В конце концов, иногда это похоже на то, чему мы учимся больше всего.
К сожалению, есть ограничения. Пока вы все еще учитесь использовать функцию, вам, например, не следует брать на себя роль учителя. Я часто читаю ответы тех, кто, казалось бы, не знает, как использовать
realloc
(т.е. принятый в настоящее время ответ! ), Рассказывая другим, как использовать его неправильно, иногда под предлогом того, что они пропустили обработку ошибок , хотя это обычная ошибка что необходимо упомянуть. Вот ответ, объясняющий, какrealloc
правильно использовать . Обратите внимание, что в ответе возвращаемое значение сохраняется в другой переменной для проверки ошибок.Каждый раз, когда вы вызываете функцию и каждый раз, когда вы используете массив, вы используете указатель. Преобразования происходят неявно, что, пожалуй, должно быть еще страшнее, поскольку именно то, что мы не видим, часто вызывает больше всего проблем. Например, утечки памяти ...
Операторы массивов - это операторы указателей.
array[x]
это действительно ярлык для*(array + x)
, который можно разбить на:*
и(array + x)
. Скорее всего, именно*
это вас смущает. Мы можем дополнительно исключить добавление из проблемы, предположив,x
что0
, таким образом,array[0]
становится,*array
потому что добавление0
не изменит значение ...... и, таким образом, мы видим, что
*array
это эквивалентноarray[0]
. Вы можете использовать один там, где хотите использовать другой, и наоборот. Операторы массивов - это операторы указателей.malloc
,realloc
и друзья не изобретают концепцию указателя, которую вы использовали все время; они просто используют это для реализации какой-либо другой функции, которая представляет собой другую форму продолжительности хранения, наиболее подходящую, когда вы хотите резких, динамических изменений размера .Жаль, что принятый в настоящее время ответ также идет вразрез с некоторыми другими очень хорошо обоснованными советами по StackOverflow , и в то же время упускает возможность представить малоизвестную функцию, которая подходит именно для этого варианта использования: гибкий массив Участники! На самом деле это довольно неверный ответ ... :(
Когда вы определяете свой
struct
, объявляйте свой массив в конце структуры без какой-либо верхней границы. Например:Это позволит вам объединить ваш массив
int
в то же распределение, что и вашcount
, и привязать их таким образом может быть очень удобно !sizeof (struct int_list)
будет вести себя так, как будтоvalue
имеет размер 0, поэтому он сообщит вам размер структуры с пустым списком . Вам все равно нужно добавить к переданному размеру,realloc
чтобы указать размер вашего списка.Еще один полезный совет - запомнить, что
realloc(NULL, x)
это эквивалентноmalloc(x)
, и мы можем использовать это для упрощения нашего кода. Например:Причина, по которой я решил использовать
struct int_list **
в качестве первого аргумента, может показаться не сразу очевидной, но если подумать о втором аргументе, любые изменения, сделанныеvalue
изнутриpush_back
, не будут видны функции, из которой мы вызываем, верно? То же самое касается первого аргумента, и нам нужно иметь возможность изменять нашarray
, не только здесь, но, возможно, также в любой другой функции / функциях, которым мы передаем его ...array
начинает ни на что не указывать; это пустой список. Его инициализация аналогична добавлению к нему. Например:PS Не забудьте,
free(array);
когда вы закончите с этим!источник
array[x]
это действительно ярлык для*(array + x)
, [...]" Вы уверены в этом ???? См. Описание их различного поведения: eli.thegreenplace.net/2009/10/21/… .array[index]
на самом делеptr[index]
переодетый ... «определение оператора индекса[]
является то , чтоE1[E2]
является идентичным(*((E1)+(E2)))
» Вы не можете опровергнуть зОеint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Вам, вероятно, понадобится#include <stdio.h>
и<stddef.h>
... Вы видите, как я написалx[lower]
(сx
целочисленным типом), а неlower[x]
? Компилятору C все равно, потому что*(lower + x)
это то же значение*(x + lower)
, что и, иlower[x]
первое, аx[lower]
второе - как. Все эти выражения эквивалентны. Попробуйте их ... убедитесь сами, если вы не можете поверить мне на слово ...gcc
илиclang
для всей вашей компиляции C, потому что вы обнаружите, что существует так много пакетов, которые используют функции C99 ...Я могу придумать несколько вариантов.
array[100]
без предварительной прогулки1-99
. И вам тоже может быть не так удобно.Трудно сказать, какой вариант лучше всего подходит в вашей ситуации. Простое создание большого массива - это, конечно, одно из самых простых решений и не должно вызывать особых проблем, если только он не действительно большой.
источник
realloc
с # 3 - выделить массив нормального размера, а затем увеличивать его, когда у вас закончится.realloc
при необходимости выполнит копирование ваших данных. Что касается вопроса OP об управлении памятью, вам просто нужноmalloc
один раз в начале,free
один раз в конце иrealloc
каждый раз, когда вам не хватает места. Это не так уж и плохо.7 * 3264 * 32 bit
похоже91.39 kilobytes
. Не так уж много по любым стандартам в наши дни;)realloc
возвратеNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Возможно, лучше всего было бы записать как:int *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
... Таким образом было бы очевидно, что все, что действительно произойдет, должно произойти раньшеNULL
присваивается значениеa->array
(если оно вообще).Основываясь на дизайне Маттео Фурланса , когда он сказал, что « большинство реализаций динамических массивов работают, начиная с массива некоторого (небольшого) размера по умолчанию, а затем, когда у вас заканчивается место при добавлении нового элемента, удваивайте размер массива ». Разница в « незавершенной работе » ниже в том, что она не увеличивается вдвое, а предназначена для использования только того, что требуется. Я также пропустил проверки безопасности для простоты ... Также, основываясь на идее brimboriums , я попытался добавить в код функцию удаления ...
Файл storage.h выглядит так ...
Файл storage.c выглядит так ...
Main.c выглядит так ...
Надеемся на последующую конструктивную критику ...
источник
malloc()
прежде чем пытаться использовать выделение. Точно так же было бы ошибкой напрямую назначать результатrealloc()
указателю на перераспределяемую исходную память; в случаеrealloc()
сбояNULL
возвращается, а код остается с утечкой памяти. Гораздо эффективнее удвоить память при изменении размера, чем добавлять по одному пространству за раз: меньше обращений кrealloc()
.int
и т. Д.) За раз. Удвоение - типичное решение, но я не думаю, что существует какое-либо оптимальное решение, подходящее для всех обстоятельств. Вот почему удвоение - хорошая идея (подойдет и другой фактор, например 1,5): если вы начнете с разумного распределения, вам может вообще не понадобиться перераспределение. Когда требуется больше памяти, разумное распределение удваивается и т. Д. Таким образом, вам, вероятно, понадобится всего один или два звонка наrealloc()
.Когда ты говоришь
вы в основном говорите, что используете «указатели», но тот, который является локальным указателем всего массива вместо указателя всей памяти. Поскольку вы концептуально уже используете «указатели» (т.е. номера идентификаторов, которые относятся к элементу в массиве), почему бы вам просто не использовать обычные указатели (например, номера идентификаторов, которые относятся к элементу в самом большом массиве: вся память ).
Вместо того, чтобы ваши объекты хранят номера идентификаторов ресурсов, вы можете заставить их хранить указатель. В основном то же самое, но гораздо более эффективно, поскольку мы избегаем превращения «массив + индекс» в «указатель».
Указатели не страшны, если вы думаете о них как о массиве индекса для всей памяти (что они и есть на самом деле)
источник
Чтобы создать массив из неограниченного количества элементов любого типа:
и как им пользоваться:
Этот вектор / массив может содержать любой тип элемента и полностью динамичен по размеру.
источник
Ну, я думаю, если вам нужно удалить элемент, вы сделаете копию массива, презирая элемент, который нужно исключить.
Предположим , что
getElement2BRemove()
,copy2TempVector( void* ...)
иfillFromTempVector(...)
вспомогательные методы для обработки вектора Темп.источник