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.
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:
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:
1 Answer 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
And now I understand scope when dealing with pointers a bit better :)
-
1you could also just make it static or global to avoid having to pass it around.dandavis– dandavis2021年02月25日 16:59:08 +00:00Commented 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 :)Chris Schmitz– Chris Schmitz2021年02月25日 17:15:27 +00:00Commented Feb 25, 2021 at 17:15
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).