The problem is that you don't copy the terminator to the destination array. Instead you end the loop when you encounter it (without copying it).
Without knowing the use-case or the need for the array (there seldom is any), don't use manual copying like that. If, for whatever reason you can't use e.g. a.c_str() or a.data() or even &a[0], then use strncpy instead:
strncpy(b, a.c_str(), sizeof b - 1); // -1 to leave space for the terminator
b[sizeof b - 1] = '\0'; // And make sure string is terminated
Do note that the guarantee of an existing terminator in the std::string object depends on the C++ standard used. Before C++11 there were no guarantees that the terminator existed in the string. In practice it still did (for simplicity's sake) but there were no guarantees by the specification.
a[i]fori > 4invokes UB.a[4]be'\0'? That's my reading of the description here: cplusplus.com/reference/string/string/operator[]