I agree that C is complicated, but this one has nothing to do with macros, flags or anything external. All you need to know to understand it is in the language itself.
C is easy, computers are complex. Sometimes these are confused. Like in this case, the syntax is easy to human-parse, but it's harder to reason about the result of the expression on different architectures and with different compilers, yet the complexity is caused by the computer, not the language.
Quite. I started out in a world of C99, endianness and differing bit packing. My early day to day involved many more unions and a lot more time wondering if SunOS cc was going to behave the same as Dynix or MSC on DOS.
It's easy to forget how much of that mindless preprocessor and make conditionality we've left behind. Sadly in exchange for hardware that's much blander now.
The rules are simple for simple projects. Then you get into things like undefined behaviours, implementation-specific behaviours, etc. Which compilers will abuse heavily for optimisations without telling you about it - for example like the case of silently removing NULL checks. Also any undefined behaviour at all in your source code allows the compiler to throw away all code after it. Without telling you about it.
Maybe the book I read was not that good (Deitel&Deitel) but the section on macros was pretty tiny and I don't think I'd have understood that macro on my own. Anyways, I think C is great, just that most books don't seem to cover the important stuff to work on serious C projects.
The macro isn't the complicated part in the OP (`e` just gets substituted with whatever the argument is). The definition the macro generates is (for most cases, needlessly) complicated, but that has nothing to do with macros or the preprocessor.
The general rules of macros are:
1. Don't use them; prefer static functions.
2. Use them as symbolic constants only.
3. Use parameterized macros only when you must use # or ## (i.e., for code abstraction), and then use them sparingly.
4. If you really insist, wrap every use of the arguments in parentheses, and be careful not to write the name of an argument somewhere where you don't mean it.
You'll get pretty far knowing next to nothing about macros (e.g. expansion phases and tokenization rules) by following the above rules.
Deitel&Deitel make some amazing books as far as I'm concerned. They were my source material through college and a very enjoyable introduction to C, C++, and Java.
(Similar misunderstanding to the "down to operator" --> https://stackoverflow.com/questions/1642028/what-is-the-name...)