use undef to manage the lifetime of your macros and not to screw with sections of code that aren't aware of your macros' existence.
int foo(int z){
//some code
}
int bar(int x){
//some code
}
int function(){
#define foo(x) bar(z)
int a = foo(2);
#undef foo
}
This trick extends to things like header options, often things like Windows.h are included and the header's defines depend on defines defined outside of the header as follows:
#define WIN32_LEAN_AND_MEAN // used in ifdef checks to not define/include rarely used stuff
#define NO_STRICT // used to allow HANDLE types to be used as void * without type errors
#define NO_MINMAX // used to avoid errors because MIN/MAX are already defined
#include <Windows.h>
this pollutes scope with macros that have no use after the header finished parsing, so you can clean up by undefing them after include:
#define WIN32_LEAN_AND_MEAN // used in ifdef checks to not define/include rarely used stuff
#define NO_STRICT // used to allow HANDLE types to be used as void * without type errors
#define NO_MINMAX // used to avoid errors because MIN/MAX are already defined
#include <Windows.h>
#undef NO_MINMAX
#undef NO_STRICT
#undef WIN32_LEAN_AND_MEAN
This makes it easier for people reading your code, especially beginners to C to know that these are options and don't have any use after the include occurs. This also allows you to do interesting C++-like behaviors like templating based on defines defined before include, without conflicts of including headers multiple times.