First of all, thank-you for this question it has an interesting wrinkle.
I ran your code with Eclipse/Microsoft C and did NOT get a segmentation error and it printed "asdf" as expected.
However, this does NOT mean or imply that you are not getting a segmentation fault. Your result implies an examination of how a compiler implements the two statements:
char *test3 = (char *) malloc(sizeof(char) * 5);
Allocates storage on the heap and sets the pointer, test3, to point to that location.
The next statement also updates the same pointer.
test3 = "asdf";
However, in this case test3 points to the literal "asdf" where-ever that literal is stored. Some compilers generate a literal pool of strings and store them somewhere in the executable, as such for some compilers these literals cannot be modified.
So why would the compiler store a literal where it cannot be accessed? Doesn't make sense, hence the question: what C compiler are you using? And what version of C is it adhering to??
To work around what might be a compiler bug, and still point test3 at a literal, try?? (Again, C compilers do differ on what and how they implement language constructs.)
const char *literal = "asdf"; // also try without a const stmt
// other code here
test3 = literal;
Finally, in the second example the storage on the heap that was malloced is being modified and is obviously addressable.
test3- like for example try to ALTER the content oftest3? Or do you mean that it crashes when you try to freetest3?free()call after the code in the first sample since you're freeing const-memory.