delete[] crashes ESP32 (Bad tail error)
Ok, so you have some misconceptions here:

strtok() will not create a new c-string/character array. It returns a pointer, which points to the place in data, where your current token begins. So dong delete[] on split does not make much sense. Also note, that strtok() will alter the c-string, that you give to it. So data will be altered, after the function executed. strtok() inserts a null character \0 at the place of the delimiter (: in this case. That way c-string functions can be provided with the pointer, that strtok()` returns, and will only read until the end of the token (since they look of the null character as end of string).

The String class is bad on Arduino (also on the ESP, but not in the same urgency), because they have very few memory for variables. The String class allocates its memory dynamically, everytime, that you do concat, assign or change the size of the string. That means, it creates and destroys arrays with different sizes rather often, which will lead to memory fragmentation. You will get small holes in your memory, which are too small for new allocations. With the time you will get more and more holes, until your memory is full. Then the Arduino crashes. Since Arduinos often have very few memory (and don't have an OS, that manages the RAM), that happens rather fast. The ESP has more memory, so it doesn't happen that fast, but the problem is still the same.
It is the same situation with every dynamic memory allocation. In the rare cases, where you don't get memory fragmentation, you can as easy use a local variable (managed automatically on the stack for you).



In this instance, if I have an array d, I can use that for something, free it, create a new one and fill it with different data. This saves having another unnecessary array.

Actually: No, not really. The array d is created somewhere at the start of the process() function and deleted at the end of it. Just like a local variable (statically allocated). The only benefit here is, that d can have a variable size (not know at compile time). Though that can also create problems with the dynamic allocation, as described above (depends on if you are doing dynamic allocation in between of the allocation and deletion).

The _realloc_and_null_array() function makes only sense, when the size is different from the size of the provided array. But then again you can run into the issue described above.



I don't like to just create variables all willy-nilly.

What do you mean with &quot;willy-nilly&quot;? You are only creating variables, that you really need. But that doesn't mean, that these variables cannot be statically allocated. A common misconception, that programmers coming from bigger platforms (like PCs) often have, is that you should keep the memory footprint as low as possible, thus using dynamic allocation to free resources, that aren't currently needed. On a PC the freed memory can be used by other programs running on it. But on an Arduino there is only one program running. Memory, that you don't use, will just be doing nothing. So keeping the memory footprint is important, when you are actually running out of memory for your program, but not important, when you still have enough memory. As dynamic memory allocation comes with big caveats on these platforms, it is mostly not really worth it.


All in all: Dynamic memory allocation comes with huge caveats. You cannot just use it like on a PC. If you still can use it depends on your code and your requirements (like on code runtime, before it gets unstable). There are situations, where you can get away with it, and others, where you don't.