Can a C macro contain temporary variables?

2019-01-25 09:07发布

I have a function that I need to macro'ize. The function contains temp variables and I can't remember if there are any rules about use of temporary variables in macro substitutions.

long fooAlloc(struct foo *f, long size)
{
   long      i1, i2;
   double   *data[7];

   /* do something */
   return 42;
}

MACRO Form:

#define ALLOC_FOO(f, size) \
{\
   long      i1, i2;\
   double   *data[7];\
\
   /* do something */ \
}

Is this ok? (i.e. no nasty side effect - other than the usual ones : not "type safe" etc). BTW, I know "macros are evil" - I simply have to use it in this case - not much choice.

9条回答
姐就是有狂的资本
2楼-- · 2019-01-25 09:17

C macros are only (relatively simple) textual substitutions.

So the question you are maybe asking is: can I create blocks (also called compound statements) in a function like in the example below?

void foo(void)
{
    int a = 42;
    {   
        int b = 42;
        {
            int c = 42; 
        } 
    }
}

and the answer is yes.

Now as @DietrichEpp mentioned it in his answer, if the macro is a compound statement like in your example, it is a good practice to enclose the macro statements with do { ... } while (0) rather than just { ... }. The link below explains what situation the do { ... } while (0) in a macro tries to prevent:

http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

Also when you write a function-like macro always ask yourself if you have a real advantage of doing so because most often writing a function instead is better.

查看更多
▲ chillily
3楼-- · 2019-01-25 09:21

First, I strongly recommend inline functions. There are very few things macros can do and they can't, and they're much more likely to do what you expect.

One pitfall of macros, which I didn't see in other answers, is shadowing of variable names.
Suppose you defined:

#define A(x) { int temp = x*2; printf("%d\n", temp); }

And someone used it this way:

int temp = 3;
A(temp);

After preprocessing, the code is:

int temp = 3;
{ int temp = temp*2; printf("%d\n", temp); }

This doesn't work, because the internal temp shadows the external.
The common solution is to call the variable __temp, assuming nobody will define a variable using this name (which is a strange assumption, given that you just did it).

查看更多
贼婆χ
4楼-- · 2019-01-25 09:21

They can. They often shouldn't.

Why does this function need to be a macro? Could you inline it instead?

查看更多
兄弟一词,经得起流年.
5楼-- · 2019-01-25 09:26

Eldar's answer shows you most of the pitfalls of macro programming and some useful (but non standard) gcc extension.

If you want to stick to the standard, a combination of macros (for genericity) and inline functions (for the local variables) can be useful.

inline
long fooAlloc(void *f, size_t size)
{
   size_t      i1, i2;
   double   *data[7];

   /* do something */
   return 42;
}


#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T))

In such a case using sizeof only evaluates the expression for the type at compile time and not for its value, so this wouldn't evaluate F twice.

BTW, "sizes" should usually be typed with size_t and not with long or similar.

Edit: As to Jonathan's question about inline functions, I've written up something about the inline model of C99, here.

查看更多
叛逆
6楼-- · 2019-01-25 09:28

A not perfect solution: (does not work with recursive macros, for example multiple loops inside each other)

#define JOIN_(X,Y) X##Y
#define JOIN(X,Y) JOIN_(X,Y)
#define TMP JOIN(tmp,__LINE__)

#define switch(x,y) int TMP = x; x=y;y=TMP

int main(){
  int x = 5,y=6;
  switch(x,y);
  switch(x,y);
}

will become after running the preprocessor:

int main(){
   int x=5,y=6;
   int tmp9 = x; x=y; y=tmp9;
   int tmp10 = x; x=y; y=tmp10;
}
查看更多
别忘想泡老子
7楼-- · 2019-01-25 09:35

There are only two conditions under which it works in any "reasonable" way.

  1. The macro doesn't have a return statement. You can use the do while trick.

    #define macro(x) do { int y = x; func(&y); } while (0)
    
  2. You only target GCC.

    #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; })
    

It would help if you explain why you have to use a macro (does your office have "macro mondays" or something?). Otherwise we can't really help.

查看更多
登录 后发表回答