Malloc, куча, стек

Уточняй.
То есть ты взял пример с максимальной защитой от всего. Но от этого он не стал совсем нечитаемым. Option и Some() глазами все просто опускают, читая, как обертку. Ты же понимаешь, что мог этого не городить? И написал ты (или ИИ тебе) таким образом нарочно, “для прикола”.

на асме всё очень просто, пример посмотри у П.Нортона

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

Ну что значит, для прикола. Если с unsafe() писать, то смысл языка терятся - проще на Си написать.

Как-то так.

Нет. Список узлов в вектор. Связи индексами. Раст наводит на такие решения. Иначе вот эта вот защита памяти. Подход Раст - не изобретай структуры в памяти… Ну или опиши для них вот такое один раз.

Добавлю: в Раст очень развита система модулей, читай библиотек. Которые написаны с полными защитами. Сам язык создан так, что им можно и нужно доверять и не делать бизнес на велосипедах. Нужна структура в памяти? Посмотри в готовом, наверняка уже есть. Не трать время на хеню. В С++ мы часто не доверяли готовым из-за потенциальных проблем с памятью. Тут ровно наоборот ищешь готовое, наследуешь, добавляеешь нужное

Ну это уже не двусвязанный список :).

Решил таки доделать начатое. Подумал - полезно иметь функции для диагностики/отладки, а так же для обучения написал две функции.
Одна “полегче” - как Вы сказали, для определения максимального сплошного куска памяти без учёта фрагментации, другая - для проверки всей памяти.

Да, код из #1 сообщ - кривой. Надо гонять в цикле, тогда сразу видно утечку.

Запускаю сразу две функции(arduino Nano), искусственно делаю фрагментацию.
Погонял, вроде норм. Если кто укажет на косяк - буду благодарен.

Спойлер

//#pragma GCC optimize("O0")


uint16_t chkUnDivMem() {
  uint16_t Bytes = sizeof(uint8_t);
  uint8_t * p = nullptr;
  uint8_t * pn;

  while (pn = (uint8_t*)realloc(p, Bytes))
  {
    p = pn;
    Bytes++;
  }

  free(p);

  return Bytes - 1;
}




void chkMem()
{
  const uint8_t Byte = sizeof(uint8_t);
  const uint8_t maxpart = 5;  //МАКСИМАЛЬНОЕ ЧИСЛО ПРОВЕРЯЕМЫХ 
  uint16_t SIZE[maxpart];   //ЧАСТЕЙ ПАМЯТИ
  uint16_t Bytes;
  uint16_t mem = 0;

  uint8_t * pm[maxpart];
  uint8_t * pm1[maxpart];
  uint8_t * p0[maxpart];
  uint8_t count = 0;

  while (count < maxpart)
  {
    Bytes = Byte;
    pm[count] = 0;//вызов realloc c pm[..] = 0 работает как malloc
    while (pm1[count] = (uint8_t*)realloc(pm[count], Bytes))
    {
      pm[count] = pm1[count];
      Bytes++;
    }
    p0[count] = pm[count];//
    SIZE[count++] = Bytes - 1;
    Bytes = 0;

  }

  for (int8_t i = count - 1; i >= 0 ; i--)
    free(p0[(uint8_t)i]);

    Serial.println("FREE MEM:");
  for (uint8_t i = 0; i < maxpart && SIZE[i]; i++ )
  {
    Serial.print(i + 1);
    Serial.print(".  ");
    Serial.println(SIZE[i]);
    mem += SIZE[i];
    SIZE[i] = 0;
  }
  Serial.print("TOTAL FREE MEM:  ");
  Serial.println(mem);
  Serial.println();
}


void setup() {

  //  volatile  uint8_t * p1 = (volatile uint8_t *) 1424;
  //  *p1 = PIND; //<----------------- НЕТ РЕАКЦИИ
 // РАЗОБРАЛСЯ, ЕЁ И НЕ ДОЛЖНО БЫТЬ В loop()
 
  Serial.begin(9600);
  Serial.println("SETUP OK");
  Serial.println();

  chkMem();

  Serial.print("UNDIVIDED MEM:  ");
  Serial.println(chkUnDivMem());
  Serial.println();
  delay(300);

  uint8_t * f0 = (uint8_t *)malloc(10);
  *f0 = PINB;
  uint8_t * f1 = (uint8_t *)malloc(100);
  *f1 = PIND;
  uint8_t * f2 = (uint8_t *)malloc(10);
  *f2 = PINC;
  uint8_t * f3 = (uint8_t *)malloc(10);
  *f3 = PINB;

  // free(f3);
  free(f2);
  // free(f1);// <--------ФРАГМЕНТИРУЕМ ПАМЯТЬ
  free(f0);

}


void loop() {
 
  chkMem();// <----- ПЕЧАТЬ ВНУТРИ ФУНКЦИИ
 
  Serial.print("UNDIVIDED MEM:  ");
  Serial.println(chkUnDivMem());
  Serial.println();
  delay(300);
}
Спойлер

Попробовал тот же код запустить в Wokwi на STM32C031C6T6 (увеличил размер пары переменных):

Спойлер

Но много не гонял, и, в железе не проверял.

Вот здесь, в самом конце стартового поста есть небольшая библиотека, которая печатаем полную информацию о памяти (AVR), включая все свободные сегменты, сколько бы их не было. Посмотрите, если интересно.

Да, спасибо, много чего есть интересного, буду разбираться

Извиняюсь, не правильно написал. Имел в виду код, о котором я говорил, что не работает с опцей -О0, а с -О3 работает. Он действительно оказался кривой.
А код из #1 сообщ. норм. Хотел отредактировать пост, но уже не было возможности.

Когда пришёл на старый форум, был совсем “зелёный”, и, просто прошёл мимо этой темы, т.к. ничего в этом не понимал, было запредельно сложно для меня. Теперь в самый раз.
Ради интереса, запустил свои функции вместе с Вашим Memory report:

Спойлер

Похоже, мои функции слишком много памяти тратят на самих себя, это большой минус, спасибо за помощь , будем учиться/разбираться дальше :face_with_monocle:

Спойлер
#include "MemoryExplorer.h"

uint16_t chkUnDivMem() {
  uint16_t Bytes = sizeof(uint8_t);
  uint8_t * p = nullptr;
  uint8_t * pn;

  while (pn = (uint8_t*)realloc(p, Bytes))
  {
    p = pn;
    Bytes++;
  }

  free(p);

  return Bytes - 1;
}

void chkMem()
{
  const uint8_t Byte = sizeof(uint8_t);
  const uint8_t maxpart = 5;  //МАКСИМАЛЬНОЕ ЧИСЛО ПРОВЕРЯЕМЫХ 
  uint16_t SIZE[maxpart];   //ЧАСТЕЙ ПАМЯТИ
  uint16_t Bytes;
  uint16_t mem = 0;

  uint8_t * pm[maxpart];
  uint8_t * pm1[maxpart];
  uint8_t * p0[maxpart];
  uint8_t count = 0;

  while (count < maxpart)
  {
    Bytes = Byte;
    pm[count] = 0;//вызов realloc c pm[..] = 0 работает как malloc
    while (pm1[count] = (uint8_t*)realloc(pm[count], Bytes))
    {
      pm[count] = pm1[count];
      Bytes++;
    }
    p0[count] = pm[count];//
    SIZE[count++] = Bytes - 1;
    Bytes = 0;

  }

  for (int8_t i = count - 1; i >= 0 ; i--)
    free(p0[(uint8_t)i]);
    Serial.println();
    Serial.println("--- chkMem()----");
    Serial.println("FREE MEM:");
  for (uint8_t i = 0; i < maxpart && SIZE[i]; i++ )
  {
    Serial.print(i + 1);
    Serial.print(".  ");
    Serial.println(SIZE[i]);
    mem += SIZE[i];
    SIZE[i] = 0;
  }
  Serial.print("TOTAL FREE MEM:  ");
  Serial.println(mem);
  Serial.println();
}
void setup() {
 Serial.begin(115200);
 Serial.println("SETUP OK");
  memoryReport("Fragmentation");
  chkMem();
   Serial.println(); 
  Serial.println("----chkUnDivMem()----");
  Serial.print("UNDIVIDED MEM:  ");
  Serial.println(chkUnDivMem());
  Serial.println();
  delay(300);
}

void loop() {
   memoryReport("Fragmentation");
   chkMem();
  Serial.println(); 
  Serial.println("----chkUnDivMem()----");
  Serial.print("UNDIVIDED MEM:  ");
  Serial.println(chkUnDivMem());
  Serial.println();
   delay(500);
}
Спойлер

Глобальные переменные используют 436 байт (21%) динамической памяти, оставляя 1612 байт для локальных переменных. Максимум: 2048 байт.