Can a C macro contain temporary variables?

冷暖自知 提交于 2019-12-18 04:40:10

问题


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.


回答1:


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.




回答2:


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.




回答3:


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:


This is mostly OK, except that macros are usually enclosed with do { ... } while(0) (take a look at this question for explanations):

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

Also, as far as your original fooAlloc function returns long you have to change your macro to store the result somehow else. Or, if you use GCC, you can try compound statement extension:

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

Finally you should care of possible side effects of expanding macro argument. The usual pattern is defining a temporary variable for each argument inside a block and using them instead:

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



回答5:


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:


Yes it should work as you use a block structure and the temp variables are declared in the inner scope of this block.

Note the last \ after the } is redundant.




回答7:


They can. They often shouldn't.

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




回答8:


If you're using c++ use inline, or use -o3 with gcc it will inline all functions for you. I still don't understand why you need to macroize this function.




回答9:


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;
}


来源:https://stackoverflow.com/questions/8764733/can-a-c-macro-contain-temporary-variables

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!