Since your first example shouldn't even compile I will assume that it is a simple typo in the question and work from the following code instead of your first example:
int main(){
char buffer[4];
char *tmp="qqqqqqqqqqqqqqqqqqqqqqqq"; // this should be a pointer
char *r;
r=strcpy(buffer,tmp);
return 0;}
Everyone's comments about undefined behavior are correct, in that it doesn't have to result in a seg. fault, but I think that these comments are missing the point of your question. So I will skip past the obvious reasons you shouldn't write code like this and focus on why it seems to work (for lack of a better word) when you use the static
keyword.
The static
keyword semantically changes the life time of your buffer
but lower down it also changes where in memory your buffer
is stored.
You've probably heard of the heap and the stack and if you haven't you should probably read up about them as they are crucial concepts to programming in C, but there are more memory regions than just those two in a C program. The stack and heap are used for dynamic memory but static memory is stored in the data and bss segments of your program.
Overflowing a buffer in different memory regions has different effects on the behavior of your program which are entirely dependent on what is stored around your buffer's memory location.
Without the static
keyword, in your first example, the buffer
is placed on the stack and is surrounded by your functions local variables as well as other information, like what point in your code to execute after the function returns. It is important to understand the stack frame a.k.a. Call stack. Since I suspect your buffer overflow investigations are inspired by buffer overflow attacks I would recommend reading this explanation about how they work.
When you overrun the buffer on the stack one possible result is that your program tries to continue from the wrong point, which may not even be executable code. If your program tries to execute things that are not code then the operating system steps in and dumps you like a cheating husband/wife. But note that this is only one possibility.
However, by making the buffer static
you are taking it out of the stack and putting it somewhere else that is pretty far away from executable code and is most likely surrounded by other data. When you overflow this buffer then you are corrupting data and not code so now the behavior of your program depends entirely on what data has been corrupted and if your program will do something bonkers because of it. It might just behave weird but won't crash or it might crash straight away depending on what was changed.
Undefined behavior is only undefined from the point of view of the standard. When you use undefined behavior in C, the behavior you get is dependent on your compiler and your machine which can do what ever they like. Computers are deterministic by nature and everything they do is defined by the code that they run so it is possible to define undefined behavior if you dig deep enough. But it's a lot safer to avoid it and it will make your code work everywhere instead of just on your machine on that obscure/obsolete compiler version...