The algorithm followed by the preprocessor is actually quite elaborate and involves stacked contexts to correctly handle nested and/or recursive macros (for instance if a token is expanded using some macro, then that macro will not be considered in expanding the resulting token). Probably the most direct way to fully understand the system, if you want to, is to read the docs and maybe even the internal docs.
But in most cases the preprocessor can be thought of as a process that does a single pass over the input stream, keeps track of all preprocessor macros encountered so far, and tries to expand each token according to these macros. If a token is encountered before the macro that's supposed to handle it is defined, the token will be left untouched.
Be aware that despite its apparent simplicity, advanced use of the preprocessor is typically not portable; things will work on a particular compiler and will break horribly on another because it doesn't expand your tokens correctly or somesuch. If your preprocessor definitions are growing out of control, consider reviewing your design to see if all these macros are needed, and then think about using a more specialized preprocessing system like m4 or similar.