Why use Macros in C? [duplicate]

2020-05-11 03:28发布

问题:

This question already has answers here:
Closed 10 years ago.

Possible Duplicate:
What are C macros useful for?

Every few months I get an itch to go learn some bit of C that my crap college programming education never covered. Today it's macros. My basic understanding of macros is they're a simple search and replace that happens on your code prior to it being compiled. I'm having trouble understanding why you'd use macros. Most of the basic examples I'm looking at are something like

TEST(a,%d);
#define TEST(a,b) printf(" The value of " #a " = " #b " \n", a)

//which expands to 
printf(" The value of a = %d \n",a);

(example from here)

From my newbie point of view, it seems like defining a new function would give you the same results. I can see how historically macros would be useful for modifying a lot of source quickly in the days before easy search and replace, but something tells me I'm missing some bigger point.

So what kind of useful things can macros do for you?

回答1:

One reason is until C99, the inline keyword was not standard in the C language. Thus macros allowed you to inline small functions. They also in some ways work like templates, ie. you don't have to specify types in the macro definition eg:

#define MAX(x,y) ((x) > (y) ? (x) : (y))

This macro is complient with integers, doubles, floats etc.



回答2:

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.



回答3:

Macros are expanded at compilation time. You are correct that they are often abused but a classic example in C would be to have a macro for writing debugging messages which (when debug mode is turned off at compilation time) doesn't generate any code so causes no slowdown.



回答4:

Sometimes you want to do logging, but only in debug mode. So you write something like:

#ifdef DEBUG
#define LOG_MSG(x) printf(x);
#else
#define LOG_MSG(X)
#endif

Sometimes you just want to turn something off or on based on a compile-time switch. E.g., my company does some co-branding of our product and partners ask to turn things off. So we'll do something like:

#ifndef SPECIAL_PARTNER_BUILD
    DoSomethingReallyAwesome();
#endif

Then build with -DSPECIAL_PARTNER_BUILD when giving the partner drops.

Among many other possible reasons.

Sometimes you just want to save typing for things you do over and over again. Some people take this to far (cough MFC cough) and write what amounts to their own language in Macros to abstract away a difficult API. This makes debugging a frikkin' nightmare.



回答5:

Macros can have many different uses other than function like things.

It is very useful to use macros for anything magic number or string related.

#define MY_MAGIC_NUM 57

/*MY_MAGIC_NUM used all through out the code*/


回答6:

C macros can generate code at compile time. This can be (ab)used to effectively create domain-specific languages with new keywords and behaviors.

One of my favorite examples is Simon Tatham’s method of implementing coroutines in C through macros. The simplest macro implemented is:

#define crBegin static int state=0; switch(state) { case 0:

Yes, with an unmatched brace. Other macros will fix that up.



回答7:

We used this type of macro in our code:

// convenience macros for implementing field setter/getter functions
#ifndef CREATE_GET_SET
#define CREATE_GET_SET(PREFIX, FIELD_NAME,FIELD_DATATYPE) \
  protected:\
    FIELD_DATATYPE PREFIX ## FIELD_NAME;\
  public:\
  inline FIELD_DATATYPE get ## _ ## FIELD_NAME(void) const\
  { \
    return(PREFIX ## FIELD_NAME); \
  } \
  inline void set ## _ ## FIELD_NAME(FIELD_DATATYPE p) \
  { \
    PREFIX ## FIELD_NAME = p; \
  }
#endif

within a class/structure you would define a variable:

CREATE_GET_SET(_, id, unsigned int);

This would define your variable and create the generic getter/setter for the code. It just makes for cleaner, consistent code generation for get/set. Sure, you can write it all out, but that's a lot of boilerplate type code NOTE: this is just one of a couple macros. I didn't post them all. You wouldn't handle say "char *" this way (where you want the set to strncpy or strcpy the data). This was just a simple demo of what you could do with a macro and some simple types.



回答8:

In performance intense scenarios, you can use macros to create "automatic" loop unrolling. Modern compilers probably do a better job with this though, but that used to be a useful application for it.



回答9:

The compiler often knows details about the target machine, so you can use conditional compilation to have one piece of code for big endian processors and another for little endian processors. That keeps the code from being bloated with code that's designed for a different processor.

A similar case is when you have assembly code for one particular system but C code for other systems.



回答10:

I think that biggest advantage comes when used as "setting file"

#define USE_FAST_AGORHITM
//#define USE_SLOW_ONE

And global "constants"

#define MAX_NUMBER_OF_USERS 100000

There are programs written mostly in macros (Check for instance Brian Gladman's AES implementation http://www.gladman.me.uk/cryptography_technology/index.php )



回答11:

On Macros, the function's code gets inserted into the caller's code stream. This can, depending on many other things, improve performance, because the optimizer can procedurally integrate the called code — optimize the called code into the caller But beware, because on macros argument types are not checked. A good reference to inline functions (if you are also using C++) and a little bit of macros is. http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.5