0

I have a websocket listener that is giving me a std::string of my message payload (I want the bytes) and I wrote a function that pulls each of the bytes out and puts them into 32 bit integer variables, adds that integer to an array of 32 bit integers, and passes back the pointer address:

uint32_t *get32BitInt(WebsocketsMessage message)
{
  std::string raw = message.rawData();
  uint32_t chunks[message.length() / 4];
  int chunkIndex = 0;
  for (int i = 0; i < message.length(); i += 4)
  {
    uint32_t chunk = 0;
    for (int j = 0; j < 4; j++)
    {
      chunk <<= 8;
      chunk |= raw.at(i + j);
    }
    chunks[chunkIndex] = chunk;
    chunkIndex++;
  }
  return chunks;
}

And I know that parsing and array building works because if I add a loop before I exit the overall function call it prints them out just fine.

parser builds array correctly

And in the function that calls the parser I can print out the address of the pointer that was passed back.

But, as soon as I try to dereference anything my microcontroller crashes:

crash

I know that I'm pretty green to pointers and I'm probably misunderstanding something, but I keep looking at docs and examples of passing arrays as pointers and it seems right.

Also, something worth noting is that it doesn't seem to be an issue with the actual number printing, because if I store the first number of the array into a variable and then try to print a line after the assignment the controller crashes after the assignment but before the next serial print:

crash at assignment

5
  • uint32_t chunks[message.length() / 4]; - I don't think, that you can do that. Array sizes have to be compile time constants. Or you need to use dynamic memory allocation. which can be bad (though on an ESP32 not as bad as on boards like the Uno).
    – chrisl
    Feb 25, 2021 at 15:16
  • @chrisl, gcc allows that
    – Juraj
    Feb 25, 2021 at 16:16
  • Just a guess: But don't you use a local array? Which ich created on the Stack (Frame) of the function? And then you use the pointer to that array, when the function has completed und the stack is reduced i.e. its stack has been free'd? Feb 26, 2021 at 14:25
  • Ah sorry, I first wrote the comment and then saw the answer. ;-). Party fail! Feb 26, 2021 at 14:27
  • @PeterPaulKiefer no worries, I appreciate the extra eye and comment :) Feb 26, 2021 at 15:32

1 Answer 1

1

Ah, one of my friends pointed out the issue. It's a scope snag:

chunks is allocated on the stack in get32BitInt By returning a pointer to chunks, you are pointing to memory that is deallocated. Think of it as pointing to a member variable of a class that has already been destroyed

when you try to dereference deallocated memory, bad things can happen. usually in debug the compiler makes sure you fail hard

the easiest way to test this is to allocate chunks outside of that function and pass it in as a reference

So yeah, I was allocating memory within the scope of a function and then blowing that memory contents away when that functions scope ended.

I refactored my code to move the array outside of the scope of the parsing function and passed the pointer in to the parsing function to get populated:

void get32BitInt(WebsocketsMessage message, uint32_t *chunks)
{
  Serial.println("--------------------------------");
  Serial.println("raw parse:");

  std::string raw = message.rawData();

  // uint32_t chunks[message.length() / 4];
  int chunkIndex = 0;

  for (int i = 0; i < message.length(); i += 4)
  {
    uint32_t chunk = 0;
    for (int j = 0; j < 4; j++)
    {
      chunk <<= 8;
      chunk |= raw.at(i + j);
    }
    chunks[chunkIndex] = chunk;
    chunkIndex++;
  }

  Serial.println("parsed chunks:");
  for (int i = 0; i < 11; i++)
  {
    Serial.print(chunks[i], HEX);
    Serial.print(" == ");
    Serial.println(chunks[i], BIN);
  }

  Serial.println("--------------------------------");
}

void addWebsocketListener()
{

  client.onMessage([&](WebsocketsMessage message) {

    uint32_t gameFrame[message.length() / 4]; // *try moving gameFrame out of scope
    get32BitInt(message, gameFrame);

    Serial.print("game frame address: ");
    Serial.println((int)&gameFrame);

    Serial.println("storing first 32 bit integer in a variable");
    uint32_t firstNumber = *gameFrame;

    Serial.print("game frame first value: ");
    Serial.println(firstNumber, HEX);

    Serial.println("printing frame:");
    for (int i = 0; i < 11; i++)
    {
      Serial.println(gameFrame[i], HEX);
    }
  });
}

And now my controller isn't crashing :D

enter image description here

And now I understand scope when dealing with pointers a bit better :)

2
  • 1
    you could also just make it static or global to avoid having to pass it around.
    – dandavis
    Feb 25, 2021 at 16:59
  • definitely. I'm kind of feeling my way around procedurally at the moment because I don't know 100% what the final shape of everything is going to be, but my plan is to refactor this into a class once I do and when I do my plan is to make this a private property. But yeah for now I'll def move this out to a global to simplify the code, that's a good idea. Thanks for the suggestion :) Feb 25, 2021 at 17:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.