The program does not change the "base address" of the array. He is not even trying.
What you pass to fn is the 256-bit memory address. It is numerically identical to a pointer that str will decay into other expressions, only printed differently. Here, the array really remains an array - applying the address operator to the array is one of the instances where the array does not break into a pointer. For example, the increment &str would increase it numerically by 256. This is important for multidimensional arrays, which, as we know, are actually one-dimensional arrays of arrays in C. When incrementing the first "two-dimensional" index, the array must translate the address to the beginning of the next "fragment" or " line ".
Now catch it. As for fn, the address you pass in points to a location containing a different address. It is not true; it indicates a sequence of characters. Printing this sequence of bytes, interpreted as a pointer, displays the byte values ββ"A", 65 or 0x41.
fn, however, believing that the specified memory contains an address, overwrites it with the address in which "kj" is in memory. Since str has enough memory to store the address, the assignment succeeds and results in a useful address at that location.
It should be noted that this, of course, is not guaranteed. The most common cause of failure should be alignment problems. str , it seems to me, is not required to align correctly for the pointer value. The standard states that function arguments must be compatible with parameter declarations. Arbitrary pointer types cannot be assigned to each other (you need to go through void pointers for this or do it).
Edit: david.pfx indicated that (even if pressed correctly) the code causes undefined behavior . The standard requires access to objects through compatible lvalues ββ(including references) in section 6.5 / 7 of the last public project. When casting and compiling properly with gcc -fstrict-aliasing -Wstrict-aliasing=2 ... gcc warns of a "punning type". The rationale is that the compiler should be free to assume that incompatible pointers do not change the same memory area; there is no need to assume that fn modifies the contents of str. This allows the compiler to optimize the reboot (for example, from memory for registration), which otherwise would be necessary. This will play a role in optimization; a likely example where a debugging session could not reproduce the error (namely, if the debugged program is compiled without optimization for debugging purposes). That being said, I would be surprised if it were not for the optimizing compiler to produce unexpected results here, so I left the rest of the answer as it is.
I have added some debugging printfs to illustrate what is going on. Here's a live example: http://ideone.com/aL407L .
#include<stdio.h>