I cannot fix malloc()

(I am Italian, but I thought that by writing in English I could get more opinions, since there are more people who speak it, so I translated some variables into English to make it clearer, I hope you understand).

This is the first time I have written in this forum and I am still not very experienced in programming, but I cannot fix this problem with malloc()

labirinto = labyrinth
rig = line
cln = column


#include <string.h>

char rig, cln, rig_cent, cln_cent;
char** labirinto;
char rig_cas, cln_cas, i, j;
bool error = false;

//------------------------------------------------------------------------------------------------------------//

void free_memory(){
  if (labirinto != NULL) {
    for (i = 0; i < rig; i++) {
      if (labirinto[i] != NULL) {
        free(labirinto[i]); //Clear each allocated row
        labirinto[i] = NULL;
      }
    }
    free(labirinto); // Free the main pointer
    labirinto = NULL;
  }
  error = false;
}

//------------------------------------------------------------------------------------------------------------//

void choice_level(){
  
  char scelta = 0, tentativi = 0;
  
  Serial.println("Scegli il livello:\n1) Molto Facile\n2) Facile\n3) Normale\n4) Difficile\n5) Nightmare");
  while(1){
    while(scelta == 0){
      if(Serial.available()){
        scelta = Serial.read() - '0';
        switch(scelta){
          case 1: rig = 5; cln = 7; Serial.println("Modalita' Molto Facile impostata."); rig_cent = 3; cln_cent = 4;
                  break;
          case 2: rig = 9; cln= 11; Serial.println("Modalita' Facile impostata."); rig_cent = 5; cln_cent = 6;
                  break;
          case 3: rig = 13; cln = 17; Serial.println("Modalita' Normale impostata."); rig_cent = 7; cln_cent = 9;
                  break;
          case 4: rig = 15; cln = 21; Serial.println("Modalita' Difficile impostata."); rig_cent = 8; cln_cent = 11;
                  break;
          case 5: rig = 19; cln = 25; Serial.println("Modalita' Nightmare impostata."); rig_cent = 10; cln_cent = 13;
                  break;
          default: if(tentativi >= 2){
                  rig = 3; cln = 5; rig_cent = 2; cln_cent = 3;
                  Serial.println("\nTi piace fare il simpatico?\nmodalita' bambino impostata\n");
                  break;
                  }
                  else{
                    scelta = 0; tentativi++; Serial.println("Numero non valido,\nscegli un numero da 1 a 5 per selezionare la difficolta'\n");
                    break;
                  }
        }
      }
    }

    free_memory();

    labirinto = (char**)malloc(rig * sizeof(char*)); // Allocate rows
    if (labirinto == NULL) {
        Serial.println("!Errore nell'allocazione della memoria per le righe!");
        continue;
    }

    for (i = 0; i < rig; i++) {
      labirinto[i] = (char*)malloc(cln); // Allocate columns for each row
      if (labirinto[i] == NULL) {
        Serial.print("!Errore nell'allocazione della memoria per la colonna!");
        error = true;
        break;
      }
    }

    if (error) {
      free_memory();  // Frees up all memory allocated so far
      continue;  //Go back to the beginning of the cycle and try again
    }

    for(i = 0; i<rig; i++){
      memset(labirinto[i], '#',cln);
    }
    break;
  }
}

//------------------------------------------------------------------------------------------------------------//

void inizio(){
  rig_cas = rig;
  cln_cas = cln;
  
  while(1){
    rig_cas = random(rig);
    cln_cas = random(cln);
    if(rig_cas == 0 || rig_cas == rig-1 || cln_cas == 0 || cln_cas == cln-1){
      labirinto[rig_cas][cln_cas] = 'S';
      break;
    }
  }
}

//------------------------------------------------------------------------------------------------------------//

void generazione_labirinto1(){
  
  char direzione;
  
  inizio();
  
  while(rig_cas != rig_cent || cln_cas != cln_cent){
    randomSeed(analogRead(0));
    direzione = random(4);
    if(direzione == 0 && rig_cas > rig_cent && (cln_cas != 0 && cln_cas != cln - 1)){
      rig_cas--;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if(direzione == 1 && cln_cas > cln_cent && (rig_cas != 0 && rig_cas != rig - 1)){
      cln_cas--;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if(direzione == 2 && rig_cas < rig_cent && (cln_cas != 0 && cln_cas != cln - 1)){
      rig_cas++;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if(direzione == 3 && cln_cas < cln_cent && (rig_cas != 0 && rig_cas != rig - 1)){
      cln_cas++;
      labirinto[rig_cas][cln_cas] = ' ';
    }
  }
  while (rig_cas != 0 && rig_cas != rig-1 && cln_cas != 0 && cln_cas != cln-1) {
    randomSeed(analogRead(0));
    char direzione = random(4);
    // Movimento casuale verso un bordo
    if (direzione == 0 && rig_cas > 0 && labirinto[rig_cas-1][cln_cas] != ' ') {  // Scava verso l'alto
      rig_cas--;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if (direzione == 1 && cln_cas > 0 && labirinto[rig_cas][cln_cas-1] != ' ') {  // Scava verso sinistra
      cln_cas--;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if (direzione == 2 && rig_cas < rig-1 && labirinto[rig_cas+1][cln_cas] != ' ') {  // Scava verso il basso
      rig_cas++;
      labirinto[rig_cas][cln_cas] = ' ';
    }
    else if (direzione == 3 && cln_cas < cln-1 && labirinto[rig_cas][cln_cas+1] != ' ') {  // Scava verso destra
      cln_cas++;
      labirinto[rig_cas][cln_cas] = ' ';
    }
  }
  labirinto[rig_cas][cln_cas] = 'F';
}

//------------------------------------------------------------------------------------------------------------//
void generazione_labirinto2(){
}

//------------------------------------------------------------------------------------------------------------//

void stampa_labirinto(){
  for(i=0; i<rig; i++){
    for(j=0; j<cln; j++){
      Serial.print(labirinto[i][j]);
      Serial.print(" ");
    }
    Serial.println("");
  }
  Serial.println("");
}

//------------------------------------------------------------------------------------------------------------//

void gioco(){
  
}

//------------------------------------------------------------------------------------------------------------//

void setup(){
  Serial.begin(9600);
  DDRB = DDRB & ~0x0F;
  randomSeed(analogRead(0));
}

//------------------------------------------------------------------------------------------------------------//

void loop(){
  choice_level();
  generazione_labirinto1();
  stampa_labirinto();
  gioco();
  while(1);
}

never mind that it's probably not very optimised and performant, but my goal was to create a labyrinth of varying difficulty with two-dimensional matrices, the choice of difficulty works perfectly, but i don't understand why when i then use malloc() to create a dynamic matrix, 40% of the time the program stops, and then when i tried to implement ways to make it so that when and if malloc() fails it frees the allocated memory and retries the allocation, nothing changed and it keeps stopping the program.
now the questions:

  1. did I do something wrong in programming ‘free_memory()’?
  2. is this not the error and is there something else in my program wrong?

Thank you in advance for any answers, if you have any questions or want me to put the rest of the code I am at your complete disposal

edit: I've added the full code, but for now you can only choose the difficulty (which changes the size of the labyrinth) and then it prints out the result. then I put a code underneath to replace in place of the code point where there is the column allocation so that it simulates an error and you don't have to wait for it to do it by itself.
I also add that at the moment I don't have Arduino available to me, so I used a software called Tinkercad to run it, I don't know if this could have affected the final result.

If I have forgotten anything, please let me know.

for (i = 0; i < rig; i++) {
  if (random(2) == 0) {  // 50% di probabilità di fallire
    labirinto[i] = NULL;
  } else {
    labirinto[i] = (char*)malloc(cln);
  }

  if (labirinto[i] == NULL) {
    Serial.print("Errore nell'allocazione della memoria per la riga ");
    libera_memoria();  // Libera eventuale memoria allocata
    continue;
  }
}

Please post a complete program that compiles. It doesn't have to be your full code. It can be a smaller demonstration version. But, it must compile and exhibit the same problem. This is called a Minimal Reproduceable Example (MRE).

The following snippet may be useful in tracking down if things are working (i.e. being free'd and not fragmenting the heap into unusable little bits) the way you think they should:

int getFreeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);
}

malloc() returns a simple pointer, not a pointer to a pointer. Restart using something like
char *ptr = (char *)malloc(size);

OP is trying to dynamically create a 2-D Array which, in fact, is a pointer to a pointer.

@the_leon, after posting the MRE as I requested, you may want to check out https://www.geeksforgeeks.org/dynamically-allocate-2d-array-c/

This still does not make the type cast correct.

A 2-D array can be implemented as a 1-D array of pointers or as a pointer to [row*col] chars.

See Methods 3 & 4 at the link I posted.

thanks, I will try to add it to my code to verify

OK, thanks for pointing it out to me

What is your motivation for wanting to use malloc()?

The purpose of using malloc() and free() is to allocate memory in a way that can be released when no longer required and then and used for some other purpose.

What other purpose do you have in this case? I don't see one.

Could you simply declare your labyrinth array at the maximum size your Arduino will allow, and if it is not all required because a smaller labyrinth is selected, not use parts of the array?

yes, I could have, but it was just because I wanted to try using a malloc() since I had recently discovered it as a command, then I saw that something didn't work and I didn't understand why since I looked it up and read that if malloc() fails the allocation returns NULL, so I added an algorithm to make it see it and try the allocation again, but it doesn't work and I wanted to understand why, more so I would understand what to do in the future when I need it