It's not exactly search and replace, it's token expansion. C macros are what every other kind of macro is in the computing world: a way to write something short and simple and have it automatically turn into something longer and more complicated.
One reason macros are used is performance. They are a way of eliminating function call overhead because they are always expanded in-line, unlike the "inline" keyword which is an often-ignored hint to the compiler, and didn't even exist (in the standard) prior to C99. For example, see the FD_ family of macros used in conjunction with the fd_sets used by select and pselect. These fd_sets are really just bitsets, and the FD_ macros are hiding bit twiddling operations. It would be annoying to write out the bit twiddling yourself every time, and a function call would be a lot of overhead for such a fast operation if it were not inlined.
Also, macros can do some things that functions cannot. Consider token pasting. Since the preprocessor runs before the compiler, it can make new identifiers for the compiler to use. This can give you a shorthand way to create lots of similar definitions, e.g.
#define DEF_PAIR_OF(dtype) \
typedef struct pair_of_##dtype { \
dtype first; \
dtype second; \
} pair_of_##dtype##_t
DEF_PAIR_OF(int);
DEF_PAIR_OF(double);
DEF_PAIR_OF(MyStruct);
/* etc */
Another thing it can do that a function could not is turn compile-time information into runtime information:
#ifdef DEBUG
#define REPORT_PTR_VALUE(v) printf("Pointer %s points to %p\n", #v, v)
#else
#define REPORT_PTR_VALUE(v)
#endif
void someFunction(const int* reallyCoolPointer) {
REPORT_PTR_VALUE(reallyCoolPointer);
/* Other code */
}
There's no way that a function could use the name of its parameter in its output like the macro can. This also demonstrates compiling out debug code for release builds.