max(a, b) and more

Summary from link1 and link2.

The “maximum” function is commonly defined as a macro in standard C as follows:

#define max(a,b) ((a) > (b) ? (a) : (b))

But this definition computes either a or b twice, with bad results if the operand has side effects. In GNU C, if you know the type of the operands (here taken as int), you can avoid this problem by defining the macro as follows:

#define maxint(a,b) \
  ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

Note that introducing variable declarations (as we do in maxint) can cause variable shadowing, so while this example using the max macro produces correct results:

int _a = 1, _b = 2, c;
c = max (_a, _b);

this example using maxint will not, since arguments _a & _b is masked by local variable _a & _b :

int _a = 1, _b = 2, c;
c = maxint (_a, _b);

This problem may for instance occur when we use this pattern recursively, like so:

#define maxint3(a, b, c) \
  ({int _a = (a), _b = (b), _c = (c); maxint (maxint (_a, _b), _c); })

If has no way to know the type of arguments. Here is use typeof and statement expressions together to define a safe “maximum” macro which operates on any arithmetic type and evaluates each of its arguments exactly once:

#define max(a,b) \
  ({ typeof (a) _a = (a); \
      typeof (b) _b = (b); \
    _a > _b ? _a : _b; })

The reason for using names that start with underscores (_) for the local variables is to avoid conflicts with variable names that occur within the expressions that are substituted for a and b (variable shadowing as in cases before). However, this still has same issue when use as max(_a, _b) as before, though it can handle any type. To address this issue, a new form of declaration syntax (__auto_type) is designed that allows to declare variables whose scopes start only after their initializers; this is a more reliable way to prevent such conflicts.

In GNU C, you may also declare the type of a variable as __auto_type. In that case, the declaration must declare only one variable, whose declarator must just be an identifier, the declaration must be initialized, and the type of the variable is determined by the initializer; the name of the variable is not in scope until after the initializer. Using __auto_type, the “maximum” macro above could be written as:

#define max(a,b) \
  ({ __auto_type _a = (a); \
      __auto_type _b = (b); \
    _a > _b ? _a : _b; })

Using __auto_type instead of typeof has two advantages:

  • Each argument to the macro appears only once in the expansion of the macro. This prevents the size of the macro expansion growing exponentially when calls to such macros are nested inside arguments of such macros.
  • If the argument to the macro has variably modified type, it is evaluated only once when using __auto_type, but twice if typeof is used.

Test cases:

#include <stdio.h>
#include <stddef.h>


#define max(a,b) ((a) > (b) ? (a) : (b))

#define maxint(a,b) \
  ({int _a = (a), _b = (b); _a > _b ? _a : _b; })

#define maxany(a,b) ({\
    typeof(a) _a = (a);\
    typeof(b) _b = (b);\
    (_a) > (_b) ? (_a) : (_b);})

#define maxsafe(a, b) ({\
    __auto_type _a = (a);\
    __auto_type _b = (b);\
    (_a) > (_b) ? (_a) : (_b);})


int main()
{
    int _a = 1, _b = 2, c;
    c = max(_a, _b);
    printf("max(_a, _b): %d\n", c);

    c = maxint(_a, _b);
    printf("maxint(_a, _b): %d\n", c);

    c = maxany(5, 6);
    printf("maxany(5, 6): %d\n", c);

    c = maxany(_a, _b);
    printf("maxany(_a, _b): %d\n", c);

    c = maxsafe(_a, _b);
    printf("maxsafe(_a, _b): %d\n", c);
}

$ ./a.out 
max(_a, _b): 2
maxint(_a, _b): 21887
maxany(5, 6): 6
maxany(_a, _b): 1293973488
maxsafe(_a, _b): 2

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章