Weird behavior when I try to use char* when reading from Serial Buffer

Hello,

I wanted to save content from Arduino's serial buffer to a character array by using pointer instead of an array. Instead of:

char newString[100];

I used:

char* newString = (char*)malloc(sizeof(char)*100);

I also wanted to check if they were equivalent so I tried:

1) Inserting "char* newString = (char*)malloc(sizeof(char)*100)" outside of the function and within the function. 2) Inserting "char newString[100];" outside and within the function.

The result was that 2) Worked in both cases but 1) only worked when I declared and initialized outside of the function. Why is this the case?

Here is the full code:

#define BUF_SIZE 100

int newStringAvailable; 
char curString[BUF_SIZE]; 
//char newString[BUF_SIZE];                                 //WORKS
//char* newString = (char*)malloc(sizeof(char)*BUF_SIZE);   //WORKS

void readSerial(char* dst)
{
  static uint8_t  putIndex = 0;
  char newString[BUF_SIZE];                                 //WORKS
  //char* newString = (char*)malloc(sizeof(char)*BUF_SIZE); //WEIRD BEHAVIOR 
  
  while (Serial.available())     
  {
    newString[putIndex] = (char)Serial.read();                          //Store  next char (byte) to newString[]
    if ((newString[putIndex] == '\n') || (putIndex >= BUF_SIZE - 2))  //end of message character or full buffer
    {
      newString[++putIndex] = '\0';  //Insert string end character
      strcpy(dst, newString);        //Copy to curString
      putIndex = 0;                  //Restart string iterator for next incoming string
      Serial.print("newString = "); Serial.println(newString); 
      Serial.print("dst = "); Serial.println(dst); 
    }
    else
    {
      newString[putIndex++];      //Put next char in new String location
      newStringAvailable = false; 
    }
  }
}

void setup() {
 Serial.begin(9600); 
}

void loop() {
  readSerial(curString); 

}

This is called a "memory leak." By malloc()ing new memory every time the function is called, you quickly use up all of the available memory. Then malloc() returns a null pointer, which is a zero. But zero is a valid memory location in the Arduino and you end up writing over whatever happened to be there at address 0-100. Most other architectures would give you a segfault error when you tried to use the null pointer.

If you must call malloc() every time, then you must also call free() to give that memory back. By using the conventional variable declaration, the compiler does this for you automatically, on the stack.

MorganS:
This is called a “memory leak.” By malloc()ing new memory every time the function is called, you quickly use up all of the available memory. Then malloc() returns a null pointer, which is a zero. But zero is a valid memory location in the Arduino and you end up writing over whatever happened to be there at address 0-100. Most other architectures would give you a segfault error when you tried to use the null pointer.

If you must call malloc() every time, then you must also call free() to give that memory back. By using the conventional variable declaration, the compiler does this for you automatically, on the stack.

Noted. Can you clarify what you mean by “conventional variable declaration”?

...conventional variable declaration...

I think what he's referring to is what happens when you define a variable within a function (i.e., not global scope, but define with function block scope). Such definitions mean that, when the function is entered and the definition is encountered, that variable becomes defined and space is allocated for it on the stack. This is transparent to you...no malloc() or free() required since it's managed for you. When your program reaches the closing brace of the the function where the definition is made, that variable goes out of scope (i.e., dies) and the stack space is automatically reclaimed. Unless the static qualifier is used for a variable with function block scope, that variable is given stack space each time the function is called but only dies (e.g., memory reclaimed) when the function is left. This is one reason why recursive function calls are risky because they eat more and more stack space each time the function is recursively called, perhaps running out of stack space before the calls are unwound.

I don't like the prrvious explanations.

The reason for the difference is that char newString[100] is a variable declaration, which can appear in a function, or outside a function if it is supposed to have global scope within that source code file.

Whereas char* newString = malloc(100) ;

is a piece of executable code, it calls the malloc( ) function, and to cut a long story short, executable code can only appear inside a function.

You can write a variable declaration outside of any function, you cannot write code which makes function calls outside of any function.

michinyon:
Whereas char* newString = malloc(100) ;

is a piece of executable code, it calls the malloc( ) function, and to cut a long story short, executable code can only appear inside a function.

You can write a variable declaration outside of any function, you cannot write code which makes function calls outside of any function.

Put that line of code out side of a function, you’ll be pleasantly surprised. Of course you’ll have to cast the void* return from malloc appropriately.

What you cannot do is use a function return value as a compile time constant, as in a dimension size when declaring an array, or as a template parameter. C++11 has an exception for functions marked constexpr, however that is not available by default in Arduino yet.