The C programming language has some specification, like n1570 (or newer ones). That specification defines in §6.2.1 the scope of an identifier.
So any C compiler should follow that specification.
How does a C compiler implements that specification requires a good book for explanations. I recommend the Dragon book.
Some simple or complex C compilers are open source. Look inside the source code of TinyCC, nwcc, Clang, or GCC to understand how they implement that specification (they have symbol tables, but details are specific to each compiler).
How does the compiler know where the variable is if there are duplicately-named variables?
It manages symbol tables, and update them when parsing blocks. Usually, a compiler build some abstract syntax tree of the compiled source code, and leafs in that tree representing variables refer to some symbol table. The GCC compiler documents its Generic Tree and GIMPLE data structures and provide dump options to output them. You could also compile your foo.c as gcc -S -O -fverbose-asm foo.c and look into the emitted assembler code foo.s.
At last, your example can be considered as poor programming style. Some coding guidelines (like MISRA-C or GNU coding standards) disallow or discourage it. Your code review process should catch such code (in my opinion, your example is a quite unreadable code).
My feeling is that single letter variables should have a very small scope - a dozen of lines at most.
I suggest to look (for inspiration) inside the C code of existing free software projects (like GNU bash or GNU make). Care has been taken to choose understandable names.
Take advantage of modern source code editors like GNU emacs or vim. You can configure them to type long identifiers with a few keyboard presses (they have auto-completion; and some input libraries like GNU readline provides that too). Since you (or your colleagues) will spend much more time in reading source code than in typing it, such an effort (naming well your variables and identifiers) is worth your valuable time.
If you use GCC as your compiler, invoke it as gcc -Wall -Wextra -g to get a lot of warnings and debug information. You could also use static source code analysis tools like Frama-C or the Clang static analyzer.
For real life software projects (for example GTK), you'll have a document specifying coding conventions, and you could write some GCC plugin checking most of them. See also the DECODER project.
For some parts of your software project, you may use C code generators like SWIG or GNU bison. In some cases, you would have your own C code generator. Then be sure to generate long C identifiers to reduce the possibility of name clashes.
Some code obfuscation tools are renaming most C identifiers. If you ship C source code without comments and with most identifiers generated like _0TwK4TkhEG the resulting C code can be compiled at your client site and would practically stay unreadable. You technically could write a code obfuscator transforming readable C code to cryptic C code.