Languages are taken way too seriously, and there are way too many bad languages out there that try to dictate the shape of your solutions in a certain way. A good language shouldn't do that in my opinion, except for boring and "solved" problems. The problem with languages is (apart from LISP maybe) that you can't abstract over their syntax - so while languages should give you useful building blocks to implement a solution, they should stay out of your way as much as possible.
To me, C is the easiest way to interface with the platforms that I develop for. It's also totally sufficient to implement the ideas that I'm working on - figuring out the data layout, the flow of code and data... It offers a concise syntax for the frequent operations - load / store, arithmetic, dereferences, and function calls. Most languages are already disqualified by not offering or discouraging nesting of data types a.k.a value types, making it way too complicated to simply copy data "anonymously", using memcpy() or similar.
Think about that - the language that people can't stop bitching about because it doesn't offer "generics" is actually the one that allows you to read and write data generically without a tremendous amount of complexity and/or slowness.
That's why my personal route is to learn ways to not need the type based polymorphism offered by more complicated languages, and to get along with just straight code that can push _any_ data payload (type) to the next endpoint - without requiring the multiplication of junk in the type system and/or in the compiled binary.
Macros aren't used that much but they would be missed a lot if they weren't there to help in some cases where we need to hack an abstraction over (lexical) syntax instead of data. I said above that you can't abstract over languages' syntax, and C macros don't really do that... They're a hack, and sometimes tremendously useful.