Goto is fine. The 'harmful' style was using them in favor of ifs and loops.
The things I've seen people do to avoid a goto are pretty awful though. If you ever use a 'do {} while (0)' just to break out of it, you should feel bad. Goto is much clearer and cleaner than nonsense like that.
I think one uses goto's for two sometimes three reasons.
1. Sometimes one feels the need to abuse exceptions. But C doesn't have exceptions. So one abuses the old goto instead.
2. Sometimes the code is far more readable if you use a goto to short circuit a complex block of code. Which will become insufferably more complicated if it has to say keep track of a trivial case. if(this and not trivial case) else this and not trivial. if(case a and not trivial case) else {if not trivial case)
3. You can use long jumps to do exception handling stuff. I've never had to actually do this.
One comment. I remember trying to read ancient code that abused goto's mostly because the programmer was desperately trying to fit everything into 4k of prom in languages that didn't support structured code. That was the kind of stuff Dijkstra was bitching about, not uses 1, 2, and 3. And actually since C has always had modern control structures goto just is not abused much in practice. Probably the opposite.
Side note.
int my_var = 3;
my_var = "abc";
Just usually generates a warning when compiled. If run my_var will usually get loaded with the address of "abc". If you follow it with the statement
printf("my_var=%s\n", my_var); // this will throw a warning
The things I've seen people do to avoid a goto are pretty awful though. If you ever use a 'do {} while (0)' just to break out of it, you should feel bad. Goto is much clearer and cleaner than nonsense like that.