In your first snippet you modify the pointer in the loop, so after the loop it will no longer point to the same location returned by the malloc call.
Look at it this way, after the call to malloc the pointer and the memory look like this:
result
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+
After the first iteration of the loop it will look like this:
result
|
v
+---+---+---+---+---+---+---+---+---+---+---+---+
| H | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+
And after the loop is done it will look like this
result
|
v
+---+---+---+---+---+---+---+---+---+---+---+----+
| H | e | l | l | o | | W | o | r | l | d | \0 |
+---+---+---+---+---+---+---+---+---+---+---+----+
The copying is made, but the pointer no longer points to the original position.
[Note: Before the copying, the memory allocated by malloc will not be "empty" or initialized in any way, I just show it like that here for simplicity's sake.]
resultthen you will see that it will work.index <= strlen(str)– 'less or equal' rather than 'less than'.strlenrepeatedly. It doesn't change, after all.\0; your resulting string is an unterminated string. This might lead to undefined behavior in subsequent code.<=is correct here, but you should explain why. (You need the copy the last\0as well)