Динамический массив (массив с динамически выделяемой памятью)

kolyn
Offline
Зарегистрирован: 18.01.2019

Есть ли принципиальная разница в использовании таких конструкций для arr1 и arr2 кроме места, где под них выделяется память? Есть ли подводные камни?

int fun()
{
  int c = 5;
  return c;
}

void setup() {
int n = fun();
int arr1[n]; // создал массив c динамически выделяемой памятью на стеке

int *arr2;
arr2 = new int[n]; // здесь тоже, но на куче 
delete[]arr2;
}

void loop() {}

 

rkit
Offline
Зарегистрирован: 23.11.2016

Теряешь производительность, проблемы с фрагментацией кучи.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Разница, безусловно, есть, а вот принципиальная она для Вас или нет, решить можете только Вы.

kolyn
Offline
Зарегистрирован: 18.01.2019

andriano пишет:

Разница, безусловно, есть, а вот принципиальная она для Вас или нет, решить можете только Вы.

А в каких случаях она может быть принципиальна? Кагбэ вопрос об этом:))

kolyn
Offline
Зарегистрирован: 18.01.2019

rkit пишет:

 проблемы с фрагментацией кучи.

Объявляется внутри блока - удаляется внутри блока. По идее не должно? 

 

rkit
Offline
Зарегистрирован: 23.11.2016

Что такое блок?

kolyn
Offline
Зарегистрирован: 18.01.2019

rkit пишет:

Что такое блок?

{}

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Программисту удобнее работать со стеком, он очистится сам, при выходе из блока. А на куче delete() нужно вызвать не забыть. Больше никакой разницы, но хороший тон - всегда выбирать путь, на котором возможность накосячить меньше. Ибо вера в свою идеальность и непогрешимость есть грех гордыни! ;))) Смертный грех! Тащемта.

kolyn
Offline
Зарегистрирован: 18.01.2019

wdrakula пишет:

Программисту удобнее работать со стеком, он очистится сам, при выходе из блока. А на куче delete() нужно вызвать не забыть. Больше никакой разницы, но хороший тон - всегда выбирать путь, на котором возможность накосячить меньше.

Спасибо!

rkit
Offline
Зарегистрирован: 23.11.2016

kolyn пишет:

rkit пишет:

Что такое блок?

{}

Почему ты решил, что менеждер кучи знает, где у тебя скобки стоят?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

rkit пишет:

kolyn пишет:

rkit пишет:

Что такое блок?

{}

Почему ты решил, что менеждер кучи знает, где у тебя скобки стоят?

Цеплючий ты, как репейник и невнимательный! Автор написал что ОН САМ (без ансамбля, лично ..ля) вызвал delete() ДО закрывающей скобки. В том примере, что он написал. И "менеджер кучи"??? Простите, кто? Может быть "старший менеджер"? ;)) (...склонив голову и приподняв монокль)

rkit
Offline
Зарегистрирован: 23.11.2016

И почему ты решил, что менеджеру кучи важно, до скобки или после? Впрочем, не напрягайся, как ты не напрягся элементарно погуглить, что это вообще такое, а сходу высказал свое высосанное из пальца особо бесполезное мнение.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Э-э-э сказали мы с Петром Ивановичем ...

wdrakula пишет:

Программисту удобнее работать со стеком

Таки да, но есть нюанс ...

wdrakula пишет:

возможность накосячить

в случае со стеком существенно выше, а главное, она неконтролируема!

Вот, что будет в случае, если памяти недостаточно?

В варианте с кучей, если писать грамотно (проверять выделилась ли память простым сравнением указателя с nullptr), то ничего страшного, просто проверка даст отрицательный результат и можно предпринять какие-то меры.

А в варианте со стеком никакой простой возможности проверить что и как там выделилось нету (ну не считать же возможностью проверки полноценный мониторинг состояния памяти), просто стек налезет на кучу ... дальше рассказывать?

Ну, и ... для ардуино неважно, но вообще-то вариант со стеком - это расширение GCC - оно не стандартизовано.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

rkit пишет:

Почему ты решил, что менеждер кучи знает, где у тебя скобки стоят?

А почему Вы решили, что в ардуине есть менеджер кучи?

kolyn
Offline
Зарегистрирован: 18.01.2019

ЕвгенийП пишет:

Ну, и ... для ардуино неважно, но вообще-то вариант со стеком - это расширение GCC - оно не стандартизовано.

Но в одном из своих сообщений вы упомянули стандарт ISO C99. Я его, конечно, не читал, но и не осуждаю:)

rkit
Offline
Зарегистрирован: 23.11.2016

Еще один великий знаток подтянулся. Нет менеджера. malloc наугад адрес блоку присваивает. Еще идиотские высказывания будут?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

rkit пишет:

И почему ты решил, что менеджеру кучи важно, до скобки или после? Впрочем, не напрягайся, как ты не напрягся элементарно погуглить, что это вообще такое, а сходу высказал свое высосанное из пальца особо бесполезное мнение.

Еще раз: найди в ардуине менеджер кучи. Не найдешь - втяни уже язык в жопу! И не в ардуине, кстати, тоже. Хамство твое прощается ровно до той поры, пока оно не превосходит твою дремучесть.

Это не Java и не Python, это С++, где все происходит по желанию и под контролем автора. Если не использовать никакого "старшего менеджера кучи", то его и не будет.

До тебя еще не дошло, что даже STL не часть языка программирования?

А так - хочешь менеджера для Ардуино?  Флаг в руки - пиши! Я даже уверен в том, есть десятки написанных, если в мусоре гитхаба покопаться. ;)))

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ЕвгенийП пишет:

В варианте с кучей, если писать грамотно (проверять выделилась ли память простым сравнением указателя с nullptr), то ничего страшного, просто проверка даст отрицательный результат и можно предпринять какие-то меры.

А в варианте со стеком никакой простой возможности проверить что и как там выделилось нету (ну не считать же возможностью проверки полноценный мониторинг состояния памяти), просто стек налезет на кучу ... дальше рассказывать?

Тут соглашусь, но мне кажется, что забыть освободить память  - более вероятная ошибка... но это мое ИМХО.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

rkit пишет:

Нет менеджера. malloc наугад адрес блоку присваивает.

Это не менеджер и даже не супервайзер и не эксклюзивный дистрибьютер. Это просто malloc, и его реализация еще у Кернигана и Ричи описана. И никак с тех пор не меняется. Куча == список свободных блоков, в котором выделяется первый подходяшего размера.

А "менеджер"  - это интеллектуальное управление кучей, учет ссылок, сбор мусора, перемещение блоков в фоновом режиме, управление режимами перемещяемый/неперемещаемый, и куча других сервисов, зависящих от свойств платформы и фантазии автора.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

kolyn пишет:

Но в одном из своих сообщений вы упомянули стандарт ISO C99. Я его, конечно, не читал, но и не осуждаю:)

А Вы точно не путаете С и С++?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:
забыть освободить память  - более вероятная ошибка...
Да, может и вероятноая, но её хоть видно. А ту - суку!

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wdrakula пишет:

Это не менеджер и даже не супервайзер и не эксклюзивный дистрибьютер.

Может он эффективный менеджер? rkit в смысле.

kolyn
Offline
Зарегистрирован: 18.01.2019

ЕвгенийП пишет:

А Вы точно не путаете С и С++?

Возможно и путаю. Серьезно. 

Т.е не все фишки, реализованные в С и предусмотренные сишными стандартами должны работать в С++? До этого думал, что ++ это самостоятельный язык, но его основа С, дополненная и расширенная...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

kolyn пишет:

Т.е не все фишки, реализованные в С и предусмотренные сишными стандартами должны работать в С++? 

Нет, не все.

А в массивах, кстати, там довольно большая разница. Если интересно, посмотрите объявление массивов в С++'17 (на стр. 220) и С'18 (на стр. 106). Как видите, там отличается не только то, что в С разрешены массивы переменной длины, а в С++ - нет. И ещё, если интересно, разберитесь в стандарте С как с такими массивами sozeof работает, тоже интересно :-)

(ссылки на эти стандарты я давал в Песочнице, номера страниц указаны по PDF - они отличаются от тех, что на самих листах написаны)

kolyn
Offline
Зарегистрирован: 18.01.2019

ЕвгенийП пишет:

Как видите, там отличается не только то, что в С разрешены массивы переменной длины, а в С++ - нет. 

Спасибо, с этим разобрался - насколько знание язЫков позволило)

Цитата:

И ещё, если интересно, разберитесь в стандарте С как с такими массивами sozeof работает, тоже интересно :-)

А с этим не очень. Единственное, опытным путем определил, что в GCC при создании массива на куче sizeof не работает, вернее показывает размер указателя. А размер массива на стеке sizeof выдает верно.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kolyn пишет:

определил, что в GCC при создании массива на куче sizeof не работает, вернее показывает размер указателя.

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

чтобы узнать число элементов массива используют от такую нехитрую консрукцию

T Array[] = {.....};
N_Size = sizeof(Array) / sizeof(T);

T - любой тип

 

Но с 

T *Array = new T[...];

такой фокус не прокатывает

kolyn
Offline
Зарегистрирован: 18.01.2019

DetSimen пишет:

Но с 

T *Array = new T[...];

такой фокус не прокатывает

А в "чистом" Си  с дин. массивом, определенном через malloc, sizeof сработает?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нет. 

в Си массив тоже == указатель на 1 элемент

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

с другой стороны, когда ты делаешь new T[...] ты уже к тому времени знаешь, скока элементов будет в массиве, число их пишется в скобках вместо многоточия.  Это может быть константа или переменная, неважно, можно просто ее сохранить как размер массива. 

kolyn
Offline
Зарегистрирован: 18.01.2019

Нарывался на холивары на других форумах, типа "Оно жишь знает, сколько памяти освободить при вызове

delete[]arr;

че, Ему жалко вернуть через sizeof?"

И правда, че Ему жалко?:)

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Ему не жалко: но как же он может вернуть Вам размер массива, если Вы запрашиваете размер указателя. Ему совесть не позволяет.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

kolyn пишет:

Нарывался на холивары на других форумах, типа "Оно жишь знает, сколько памяти освободить при вызове

delete[]arr;

че, Ему жалко вернуть через sizeof?"

И правда, че Ему жалко?:)

Это от непонимания что такое sizeof и что такое delete. Ему может и не жалко, тока физической возможности не имеет.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Или сделай свой vector для ардуины, с размером, покером и переодетыми стюардессами

kolyn
Offline
Зарегистрирован: 18.01.2019

DetSimen пишет:

Или сделай свой vector для ардуины, с размером, покером и переодетыми стюардессами

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

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kolyn пишет:

Подскажите вариант удобной и наиболее правильной реализации для Ардуины

Мой. :)

kolyn
Offline
Зарегистрирован: 18.01.2019

DetSimen пишет:

kolyn пишет:

Подскажите вариант удобной и наиболее правильной реализации для Ардуины

Мой. :)

Вы слишком скромный:)) Не показываете:))))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Дак он для внутренних нужд. На самом деле не вижу ничего сложного самому написать простейший дин.массив.  Надо только определиться с тем, что он должен уметь. Как тока сформулируешь хотелки, напиши их здесь, мы его по шагам и напишем. В лиарьном времени, всем миром навалимся. 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

kolyn]</p> <p>[quote=DetSimen пишет:
для Ардуины

Еспрессив (ESP8266 и ESP32) - тоже ардуина и там есть полный STL ;))). Для АВР нужно? - так и говори. У Деда попроси.

kolyn
Offline
Зарегистрирован: 18.01.2019

wdrakula пишет:

У Деда попроси.

Да, выпросишь у него! Он самого писать заставляет! А я на шару хотел выклянчить, готовое:))))

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Я тебе предложил всего лишь написать вместе, по твоим хотелкам, как ты его видишь

b707
Offline
Зарегистрирован: 26.05.2017

DetSimen пишет:

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

вот-вот, главное - определится для чего он нужен, а то может и писать ничего не надо.

Мне в конкурсе ничего кроме malloc() и free не понадобилось

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

kolyn пишет:
А я на шару хотел выклянчить, готовое:))))
Нипапёрла :-(((

kolyn
Offline
Зарегистрирован: 18.01.2019

DetSimen пишет:

Я тебе предложил всего лишь написать вместе, по твоим хотелкам, как ты его видишь

Я на столько "новичОк", что о std::vector узнал из интернетов (русскоязычных) во время решения задачи конкурса. А Вы предлагаете мне мои хотелки сформулировать.

Хочу "щоб було як отам" и даже лучше. А где это применить - потом придумаю:)

На самом деле мне нужен был всего лишь массив, в который я мог занести значения "на лету" и вернуть мне кол-во этих значений - т.е. надо

array.size();
array.resize();

и память чтоб не фрагментировал при увеличении размера. Вы мне идею для личного "конкурса" подкинули. Попробую.

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kolyn пишет:

и память чтоб не фрагментировал при увеличении размера. 

Так не бывает

kolyn
Offline
Зарегистрирован: 18.01.2019

DetSimen пишет:

Так не бывает

Тогда задача облегчается...

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
void setup() {
	Serial.begin(115200);
	delay(250);
	Serial << "Start" << eoln;

	TArray<int> intArray(8);

	pinMode(LED_BUILTIN, OUTPUT);

	lastMillis = millis();

	Serial << "Array Items" << eoln;

	for (uint8_t i = 0; i < intArray.GetSize(); ++i) {
		intArray[i] = 10 * i;
	}

	for (uint8_t i = 0; i < intArray.GetSize(); ++i) {
		if (i > 0) Serial << ", ";
		Serial << intArray[i];
	}
	Serial << eoln;

	intArray += 1000;

	for (uint8_t i = 0; i < intArray.GetSize(); ++i) {
		if (i > 0) Serial << ", ";
		Serial << intArray[i];
	}
	Serial << eoln;

	intArray.SetNewSize(16);

	for (uint8_t i = 0; i < intArray.GetSize(); ++i) {
		if (i > 0) Serial << ", ";
		Serial << intArray[i];
	}
	Serial << eoln;

}

 

Вывод: 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

6 строчка обьявление массива, 

14-16  первоначальное заполнение массива

24 - добавление к нему еще элемента 1000

32 - увеличение размера с сохранением предыдущих данных

 

kolyn
Offline
Зарегистрирован: 18.01.2019

Да, все так. Осталась малость - TArray.h написать .

ПС код я понял сразу

ППС 32 строка - создаете новый, переписываете, старый удаляете?

kolyn
Offline
Зарегистрирован: 18.01.2019
TArray<int> intArray(8);

При объявлении массив создаете ровно 8 или с запасом (например 12)?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kolyn пишет:

ППС 32 строка - создаете новый, переписываете, старый удаляете?

Естественно

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

kolyn пишет:

TArray<int> intArray(8);

При объявлении массив создаете ровно 8 или с запасом (например 12)?

ровно 8. 

На самом деле, это массив, его переразмещать нужно редко, по самому определению массива.  Если нужно добавлять/удалять элементы, надо делать список, длина которого заранее не известна.  А там уже можно импровизировать с размерами.  Например, в расчете на частое пополнение списка, можно память хапать кратно, допустим, 8 или 16 элементам.  Если при очередном пополнении вышел за макс. число элементов, то добавлять естес-сно не 1, а сразу 8-16.  Код усложняется не намного