assert() is very useful (and commonly used) in debugging new code or unit testing code. You build it debuggable, run your tests, and if an assert (used for range/bounds checking, valid states and data, etc) triggers, then you can run the code in the debugger and see what is going on directly so it can be fixed.
You decide some condition that should never, ever happen and assert is used to abort your code at that point if it does happen, so you definitely know that the very bad thing happened (and then you fix it).
MACROs are a kind of find-and-replace feature with some additional useful benefits. First, they can take parameters which will be replaced inside the MACRO's body. But be warned, they should not be used as substitute for functions, i.e., if you can do it with a function, use a function because MACROs can be dangerous in the sense that they can introduce weird bugs. Function-like MACROs can be useful for generating code that could not be generated otherwise (via templates or functions). Additionally, MACROs can be used in simple pre-processor logic statements (#if, #ifdef, etc..) which can allow you to turn on and off certain sections of code depending on the value (or existence) of certain MACRO symbols.
Asserts are used for checking conditions that should never occur unless there is a bug in the code, i.e., they're sanity checks. The point is that there are a number of checks, such as range checks, that don't need to done when you have completely bug-free code. But until you know for sure that you don't have bugs, you need to do some debugging and quality assurance (unit-tests, etc..). During that debugging phase, it's useful to perform a number of checks everywhere to catch as many bugs as possible, but one you are done debugging, you don't want to have to go through the code and remove all those checks. So, asserts are checks that are only enabled when the code is compiled in "debug" mode, and get removed by the compiler when you compile in "release" mode.
The NDEBUG macro is used to mark whether the code is being compiled in debug mode or not, i.e., if NDEBUG is defined, it means it's not in debug mode. The only real thing that it does is remove the asserts, but I guess you could use it for other things too.