When a function runs in C, it allocates space for all the local variables its going to need. If the variables are allocated all next to each other at the top of the function its easy to see how it works:
void foo(){
int x;
int y;
y = 1;
x = y + 2;
return x + y;
}
If the variables are declared inside inner blocks of the function, what the compiler does is "lift" those variable declarations to the top of the function. If there are variables with clashing names, the compiler renames things for you so they reference the correct variable.
// This is what you write
void original(){
int x = 0;
while(1){
int x = 1;
}
}
// This is what the compiler "sees"
void lifted(){
int x1;
int x2;
x1 = 0;
while(1){
x2 = 0;
}
}
In your case, your code is behaving like this one:
void function(void)
{
int i;
while(1)
{
i = 0;
i += 1;
if(i == 500) break;
}
}
In this version, its clear that the i is being reset to 0 all the time, which is why the loop will run forever.
As for if declaring variables in inner scopes is a good practice or not, it doesn't have to do with memory usage but with the scope of your variable names. In general its a good thing to keep your variables confined to inner scopes if possible (for the same reason why local variables are preferred to global ones). That said, you always have to initialize your variables before the loop and not inside it. In this case, its about removing bugs and not about being a best practice.