When your code calls a function, the called function usually allocates memory for its variables, just before the memory where the return address is stored (the address of the caller).
Let's say you have an array int a[100], and a double variable x. The compiler may quite reasonably allocate memory for x either just before or just after the memory for a. In the first case, setting a [-1] = 100; a [-2] = 1000; will likely change x. In the latter case, setting a [100] = 100; a [101] = 1000; will do the same thing. Trying to store into an array, using an index that is too small or too large, is called a "buffer overflow".
Now if you store a [102] = 0; or a [103] = 0; then chances are that this overwrites the memory where the caller's return address was stored. And if the function tries to return, the processor will start running code at the address that you stored into a [102] and a [103] with all kinds of negative consequences.