Strictly speaking you have undefined behaviour and any observed behaviour (wrong or seemingly partially correct) is explained by that.
For details and solutions see the other answers.
End of answer.
Now lets look at a speculation on why you might in your environment end up with specifically the output you observe.
Assumption, the memory for your arrays
char a[] = "abc";
char b[2]
looks like an often seen habit of linkers of how to arrange variables:
b[0] non-initialised
b[1] non-initialised
a[0] = 'a'
a[1] = 'b'
a[2] = 'c'
a[3] = '\0'
Note the four (not three) elements of a
and the terminator 0.
Your loop, right in the first iteration, attempts to write to the non-existing b[2]
.
This is already what causes undefined behaviour. Clean discussion ends here.
Let's continue speculating.
Your loop unintentionally writes one place beyond the existing b[1]
and ends up clobbering a[0]
. By chance it writes the value which happens to be already there, so no change there.
Your loop continues to write, now to existing entries of b
.
The speculated result is
b[0] = 'c
b[1] = 'b'
a[0] = 'a' = 'a'
a[1] = 'b'
a[2] = 'c'
a[3] = '\0'
and the loop ends.
Then you try to output a
and b
.
This is done by outputting all characters found consecutively from the start of the arrays, until a terminator 0 is found.
For a
this (luckily in case of the "a") is "abc\0", all from a
.
For b
this is "bc" from b
, followed (on the search for a 0) by "abc\0" from a
.
Note that the seemingly correct "a" already is incorrectly from a
, not from b
.