Говнокод по пятницам. Эпизод 1. Бездонная память

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

Итак, приступим!

В этих ваших Интернетах постоянно вылазят какие-то фрики и утверждают, что якобы оператор new ни в коем случае нельзя использовать без парного ему delete! Якобы, от этого неминуемо произойдёт утечка памяти и со временем память может просто закончиться. Так оно и было, но сейчас это устарело! Память бесконечна, если использовать новую технологию, которая пока добавлена экспериментально (и потому не особо документирована), но «нормальные пацаны» в курсе!

Как это сделать? Как получить доступ к бесконечной памяти? Нужно выполнить несколько простых шагов:

  • определить тип данных с фиксированным названием _endless_memory_type_. Это может быть любой тип – память из бесконечного пула будет выделяться блоками этого типа;
  • определить блок данных типа _endless_memory_type_ с фиксированным названием _endless_memory_ – это «образец» блоки из бесконечной памяти будут создаваться по образу и подобию этого образца;
  • зарегистрировать определённые в п.п.1 и 2 тип и образец в системе;

Всё! Теперь можно сколько угодно раз запрашивать блоки памяти типа _endless_memory_type_ и, если при каждом запросе указывать созданный образец, то память будет выделяться столько раз, сколько запросим. Никакого исчерпания памяти и никаких delete! Всё, что нужно – не забывать указывать образец при запросе!

Ниже приведён пример кода, где всё это делается и достаточно подробно прокомментировано. Код для Arduino Uno. IDE у меня 2.2.1. В более поздних версиях работает точно. В ранних тоже работает, но только в тех, где используется достаточно новый компилятор (в который эту недокументированную фичу уже добавили). Кстати, не вижу причин, почему бы этому не работать на RSP или ARM, лишь бы компилятор свежий был.

Запускайте, смотрите
/////////////////////////////////////////////////
//
// Arduino UNO
//	IDE - проверялось в 2.2.1, но подойдёт любая более или менее современная
//
#include <new.h>

// Размер буфера - 1 килобайт
static constexpr size_t BufferLenght = 1024;

//
//	создание типа блока бесконечной памяти
//
typedef uint8_t _endless_memory_type_ [BufferLenght];
//
//	Шаблон блока
//
_endless_memory_type_ _endless_memory_;
//
//	регистрация созданных типа и шаблона
//
#pragma push_macro("_endless_memory_type_ _endless_memory_")
//
// Размер типа _endless_memory_type_ в байтах
//
static constexpr size_t sizeEMT = sizeof(_endless_memory_type_);

//
//	Запрос блока памяти типа _endless_memory_type_ по шаблону _endless_memory_
//	(никаких delete !!!)
//
void bottomLess(void) {
	//
	// Выделим память 
	//
	uint8_t * newBuff = new (_endless_memory_) _endless_memory_type_;
	//
	// Проверим, выделилась ли
	//
	if (! newBuff) { // Если память не дали
		Serial.println("It sucks! Memory exhausted!");
		return;
	}
	//
	//	ПРОВЕРКА, всё ли нормально с выделенной памятью
	//
	//	Для этого возьмём случайное байтовое значение
	//
	const uint8_t content = random(256); 
	Serial.print("Content: ");
	Serial.print(content);
	//
	// и заполним выделенный буфер этим значением
	//
	memset(newBuff, content, sizeEMT);
	//
	// Проверим как заполнилось
	// Для этого напечатаем 8 случайных байтов из буфера 
	// Все они должны быть равны тому, чем заполняли
	//
	Serial.print("; Random bytes:");
	for (uint8_t i = 0; i < 8; i++) {
		Serial.print(' ');
		Serial.print(newBuff[random(sizeEMT)]);
	}
	Serial.println();
}

void setup(void) {
	Serial.begin(9600);
	//
	//	Напечатаем размер блока, который будем запрашивать в байтах
	//	просто для контроля, сколько на самом деле запрашиваем
	//
	Serial.print("Size of the _endless_memory_type_ in bytes: ");
	Serial.println(sizeEMT);
	//
	//	Выполним 50 запросов блока типа _endless_memory_type_ при помощи new
	//	При этом никаких delete не делаем.
	//
		for (uint8_t i = 0; i < 50; bottomLess(), i++);
	//
	// 50 раз успешно запросили килобайтный буфер. 
	//	Можно продолжать, она и 100 и 1000 раз выделится память бесконечна!
}

void loop(void) {}

а вот и

Результат в мониторе порта
Size of the _endless_memory_type_ in bytes: 1024
Content: 167; Random bytes: 167 167 167 167 167 167 167 167
Content: 77; Random bytes: 77 77 77 77 77 77 77 77
Content: 152; Random bytes: 152 152 152 152 152 152 152 152
Content: 214; Random bytes: 214 214 214 214 214 214 214 214
Content: 243; Random bytes: 243 243 243 243 243 243 243 243
Content: 97; Random bytes: 97 97 97 97 97 97 97 97
Content: 20; Random bytes: 20 20 20 20 20 20 20 20
Content: 72; Random bytes: 72 72 72 72 72 72 72 72
Content: 155; Random bytes: 155 155 155 155 155 155 155 155
Content: 254; Random bytes: 254 254 254 254 254 254 254 254
Content: 108; Random bytes: 108 108 108 108 108 108 108 108
Content: 152; Random bytes: 152 152 152 152 152 152 152 152
Content: 54; Random bytes: 54 54 54 54 54 54 54 54
Content: 15; Random bytes: 15 15 15 15 15 15 15 15
Content: 201; Random bytes: 201 201 201 201 201 201 201 201
Content: 221; Random bytes: 221 221 221 221 221 221 221 221
Content: 0; Random bytes: 0 0 0 0 0 0 0 0
Content: 215; Random bytes: 215 215 215 215 215 215 215 215
Content: 245; Random bytes: 245 245 245 245 245 245 245 245
Content: 2; Random bytes: 2 2 2 2 2 2 2 2
Content: 9; Random bytes: 9 9 9 9 9 9 9 9
Content: 180; Random bytes: 180 180 180 180 180 180 180 180
Content: 215; Random bytes: 215 215 215 215 215 215 215 215
Content: 193; Random bytes: 193 193 193 193 193 193 193 193
Content: 240; Random bytes: 240 240 240 240 240 240 240 240
Content: 127; Random bytes: 127 127 127 127 127 127 127 127
Content: 27; Random bytes: 27 27 27 27 27 27 27 27
Content: 242; Random bytes: 242 242 242 242 242 242 242 242
Content: 232; Random bytes: 232 232 232 232 232 232 232 232
Content: 39; Random bytes: 39 39 39 39 39 39 39 39
Content: 53; Random bytes: 53 53 53 53 53 53 53 53
Content: 30; Random bytes: 30 30 30 30 30 30 30 30
Content: 50; Random bytes: 50 50 50 50 50 50 50 50
Content: 202; Random bytes: 202 202 202 202 202 202 202 202
Content: 202; Random bytes: 202 202 202 202 202 202 202 202
Content: 154; Random bytes: 154 154 154 154 154 154 154 154
Content: 232; Random bytes: 232 232 232 232 232 232 232 232
Content: 56; Random bytes: 56 56 56 56 56 56 56 56
Content: 195; Random bytes: 195 195 195 195 195 195 195 195
Content: 2; Random bytes: 2 2 2 2 2 2 2 2
Content: 115; Random bytes: 115 115 115 115 115 115 115 115
Content: 135; Random bytes: 135 135 135 135 135 135 135 135
Content: 112; Random bytes: 112 112 112 112 112 112 112 112
Content: 107; Random bytes: 107 107 107 107 107 107 107 107
Content: 124; Random bytes: 124 124 124 124 124 124 124 124
Content: 231; Random bytes: 231 231 231 231 231 231 231 231
Content: 50; Random bytes: 50 50 50 50 50 50 50 50
Content: 147; Random bytes: 147 147 147 147 147 147 147 147
Content: 133; Random bytes: 133 133 133 133 133 133 133 133
Content: 23; Random bytes: 23 23 23 23 23 23 23 23

Как видите, в коде мы 50 раз успешно запрашиваем куски памяти по 1 килобайту при помощи new. Никаких delete в коде нет. Можно и больше запросить – никто не мешает. Всё отлично работает. А так, между прочим, Вы ещё помните, сколько всего памяти у Uno? То-то!

Как это работает?

Ну, за подробностями к физикам, я лишь слышал, что при работе тактовых генераторов на частотах от 1МГц и выше, вокруг контроллера возникает устойчивое торсионное поле. Оно микромощное, но этой микромощности достаточно для связи с торсионными полями Метавселенной, что позволяет организовать хранение практически неограниченных объёмов данных с мгновенным доступом к ним. Теоретически, конечно, память не бесконечна, её размер ограничен информационной ёмкостью Метавселенной, но это такие величины, что … ну, Вы поняли!

Так это или не так, но оно ведь работает! Вы же сами запускали и видели!

С пятницей, дорогие коллеги!

2 лайка

Такая безобидная строка № 6 и такой подвох …
66 строку меняем на:

Serial.print(" newBuff=");
Serial.println((uint16_t)newBuff, HEX);

и видим что все массивы лежат по одному адресу …

Результат:
Size of the _endless_memory_type_ in bytes: 1024
Content: 167; Random bytes: 167 167 167 167 167 167 167 167 newBuff=16A
Content: 77; Random bytes: 77 77 77 77 77 77 77 77 newBuff=16A
Content: 152; Random bytes: 152 152 152 152 152 152 152 152 newBuff=16A
Content: 214; Random bytes: 214 214 214 214 214 214 214 214 newBuff=16A
Content: 243; Random bytes: 243 243 243 243 243 243 243 243 newBuff=16A
Content: 97; Random bytes: 97 97 97 97 97 97 97 97 newBuff=16A
Content: 20; Random bytes: 20 20 20 20 20 20 20 20 newBuff=16A
Content: 72; Random bytes: 72 72 72 72 72 72 72 72 newBuff=16A
Content: 155; Random bytes: 155 155 155 155 155 155 155 155 newBuff=16A
Content: 254; Random bytes: 254 254 254 254 254 254 254 254 newBuff=16A
Content: 108; Random bytes: 108 108 108 108 108 108 108 108 newBuff=16A
Content: 152; Random bytes: 152 152 152 152 152 152 152 152 newBuff=16A
Content: 54; Random bytes: 54 54 54 54 54 54 54 54 newBuff=16A
Content: 15; Random bytes: 15 15 15 15 15 15 15 15 newBuff=16A
Content: 201; Random bytes: 201 201 201 201 201 201 201 201 newBuff=16A
Content: 221; Random bytes: 221 221 221 221 221 221 221 221 newBuff=16A
Content: 0; Random bytes: 0 0 0 0 0 0 0 0 newBuff=16A
Content: 215; Random bytes: 215 215 215 215 215 215 215 215 newBuff=16A
Content: 245; Random bytes: 245 245 245 245 245 245 245 245 newBuff=16A
Content: 2; Random bytes: 2 2 2 2 2 2 2 2 newBuff=16A
Content: 9; Random bytes: 9 9 9 9 9 9 9 9 newBuff=16A
Content: 180; Random bytes: 180 180 180 180 180 180 180 180 newBuff=16A
Content: 215; Random bytes: 215 215 215 215 215 215 215 215 newBuff=16A
Content: 193; Random bytes: 193 193 193 193 193 193 193 193 newBuff=16A
Content: 240; Random bytes: 240 240 240 240 240 240 240 240 newBuff=16A
Content: 127; Random bytes: 127 127 127 127 127 127 127 127 newBuff=16A
Content: 27; Random bytes: 27 27 27 27 27 27 27 27 newBuff=16A
Content: 242; Random bytes: 242 242 242 242 242 242 242 242 newBuff=16A
Content: 232; Random bytes: 232 232 232 232 232 232 232 232 newBuff=16A
Content: 39; Random bytes: 39 39 39 39 39 39 39 39 newBuff=16A
Content: 53; Random bytes: 53 53 53 53 53 53 53 53 newBuff=16A
Content: 30; Random bytes: 30 30 30 30 30 30 30 30 newBuff=16A
Content: 50; Random bytes: 50 50 50 50 50 50 50 50 newBuff=16A
Content: 202; Random bytes: 202 202 202 202 202 202 202 202 newBuff=16A
Content: 202; Random bytes: 202 202 202 202 202 202 202 202 newBuff=16A
Content: 154; Random bytes: 154 154 154 154 154 154 154 154 newBuff=16A
Content: 232; Random bytes: 232 232 232 232 232 232 232 232 newBuff=16A
Content: 56; Random bytes: 56 56 56 56 56 56 56 56 newBuff=16A
Content: 195; Random bytes: 195 195 195 195 195 195 195 195 newBuff=16A
Content: 2; Random bytes: 2 2 2 2 2 2 2 2 newBuff=16A
Content: 115; Random bytes: 115 115 115 115 115 115 115 115 newBuff=16A
Content: 135; Random bytes: 135 135 135 135 135 135 135 135 newBuff=16A
Content: 112; Random bytes: 112 112 112 112 112 112 112 112 newBuff=16A
Content: 107; Random bytes: 107 107 107 107 107 107 107 107 newBuff=16A
Content: 124; Random bytes: 124 124 124 124 124 124 124 124 newBuff=16A
Content: 231; Random bytes: 231 231 231 231 231 231 231 231 newBuff=16A
Content: 50; Random bytes: 50 50 50 50 50 50 50 50 newBuff=16A
Content: 147; Random bytes: 147 147 147 147 147 147 147 147 newBuff=16A
Content: 133; Random bytes: 133 133 133 133 133 133 133 133 newBuff=16A
Content: 23; Random bytes: 23 23 23 23 23 23 23 23 newBuff=16A

Ну, вот, пришёл поручик Фома и всю Метавселенную … :blush:

Так он жеж командир…тут всё чётко…упал…отжался…вижу уже работаешь )))

Интересно, я один здесь не понимаю, почему проходит проверка
стр.40 ? И , почему память таки выделяется?
Если не затруднит, объясните пожалуйста!

Навеяло …

:slight_smile:

Ну, конечно, не затруднит. Это планировалось. Потому эта тема и в “программировании”, а не в ЧЧ. Но, давайте дадим народу подумать хотя бы в течение пятницы.

Да и Вы посмотрите спокойно. Тут ведь оно, как в любом фокусе, иллюзионист тратит больше усилий на отвлечение внимания публики от главного действия, чем на само действие. Попробуйте, например, отбросить весь бред все научные объяснения из текста и комментариев к программе, и сосредоточиться на собственно коде. Сразу виднее станет :slight_smile:

Я подумал.
Можно излагать или ждать?

Это что-то пока мне недоступное, недоучился . Со старым добрым malloc такие штуки не проходят

uint8_t * newBuff = malloc(sizeEMT);
Спойлер

Колитесь. Заодно уж? объясняйте для чего это нужно и где применяется (ну, кроме обфускации говнокода) :slight_smile:

Вобщем, у new есть такая фича, как указание расположения.

С ней вызов оператора выглядит так: new (placement-args) type

Энтот placement arg через крекс-пекс-фекс передается в функцию выделения памяти дополнительным параметром. При этом способе вызова, new просто возвращает placement args как результат выполнения. Реального выделения памяти не производится.

В силу того, что placement arg в данном случае есть массив, подслащенный typedef-ом, то в new передается его сущность - указатель на начало, на его нулевой элемент (для дельфистов и гуманитариев - на первый). И, при полном бездействии new, значение указателя просто копируется в newBuff. Поэтому адрес в newBuff всегда один и тот же.

Кратенько же:

If placement-args are provided, they are passed to the allocation function as additional arguments. Such allocation functions are known as “placement new”, after the standard allocation function void* operator new(std::size_t, void*), which simply returns its second argument unchanged. This is used to construct objects in allocated storage

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

Спасибо! С Вашего позволения немного дополню.

Да, аллокатор вида

void* operator new(std::size_t size, void* ptr) noexcept;

Возвращает свой второй аргумент и больше ничего не делает. Возможно, в будущем он будет что-то делать ещё (он reserved) но сейчас только возвращает свой аргумент. Стандарт 20-го года говорит, что программист не должен писать его сам, он уже есть в системе и менять его нет смысла, да и опасно – в следующих стандартах он может начать что-то ещё делать.

В Ардуино Вы можете полюбоваться на него в файле:

Arduino15\packages\arduino\hardware\avr\1.8.6\cores\arduino\new

(именно так – без расширения)

Теперь для чего это нужно?

Это практически единственный способ создать объект (по-взрослому создать – с отработкой конструкторов и всеми делами), разместив его в той памяти, в которой мы хотим, а не где попало. Вот, если это в программе понадобилось – пригодится такая форма new.

Кстати, есть соответствующий delete.

void operator delete(void* ptr, void*) noexcept;

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

То есть «я тут всё, уборка за вами» (и г@вно в раковине уберите)?

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

1 лайк

Кстати, тут есть более забавный вопрос: можно ли написать совсем свой new? Например, вместо параметра типа void * использовать какой-то свой или даже несколько параметров? Что при этом будет с выделением памяти? Что с конструкторами?

Вопрос реально интересный. Если он интересен кому-то ещё, кроме меня, могу попробовать на него ответить (но, уже не сегодня).

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

Ну и в деструкторе “прописать” закидывание области памяти ху… иксами, к примеру, Чтобы соседний процесс не зааллочил брошенный кусок и не начал копаться во вторичном продукте, вылавливая из него пароли и cvv?

Такой замах у комитета стандартов?

Да, нет, всё проще, замах на параллельные машины, а там память – памяти рознь: своя, чужая, разделяемая быстрая, разделяемая медленная…

Хорошая тема, помню блинк от Пертовича, с исключением в “текстовкю строку” :+1:

Преаллоцировали сегмент в известной области, защищенной от посягательств соседей, а потом программист фигачит new, как руки помнят, не попадая при этом в щекотливые ситуации?

А на всяких там майнфреймах разве это не было уже давным-давно обкатано?

Ничего нового, пацанского, со времен ДОС не придумано, ты должен выйти на “'ядро” , уткуда ты выйдешь, хоть с с++,с VM, хоть с басика, похркен.

Крис каспера помню, и легенду MsRem, со вторым не все так однозначно…