Macros are simple string replacements

suggest change

Macros are simple string replacements. (Strictly speaking, they work with preprocessing tokens, not arbitrary strings.)

#include <stdio.h>

#define SQUARE(x) x*x

int main(void) {
    printf("%d\n", SQUARE(1+2));
    return 0;
}

You may expect this code to print 9 (3*3), but actually 5 will be printed because the macro will be expanded to 1+2*1+2.

You should wrap the arguments and the whole macro expression in parentheses to avoid this problem.

#include <stdio.h>

#define SQUARE(x) ((x)*(x))

int main(void) {
    printf("%d\n", SQUARE(1+2));
    return 0;
}

Another problem is that the arguments of a macro are not guaranteed to be evaluated once; they may not be evaluated at all, or may be evaluated multiple times.

#include <stdio.h>

#define MIN(x, y) ((x) <= (y) ? (x) : (y))

int main(void) {
    int a = 0;
    printf("%d\n", MIN(a++, 10));
    printf("a = %d\n", a);
    return 0;
}

In this code, the macro will be expanded to ((a++) <= (10) ? (a++) : (10)). Since a++ (0) is smaller than 10, a++ will be evaluated twice and it will make the value of a and what is returned from MIN differ from you may expect.

This can be avoided by using functions, but note that the types will be fixed by the function definition, whereas macros can be (too) flexible with types.

#include <stdio.h>

int min(int x, int y) {
    return x <= y ? x : y;
}

int main(void) {
    int a = 0;
    printf("%d\n", min(a++, 10));
    printf("a = %d\n", a);
    return 0;
}

Now the problem of double-evaluation is fixed, but this min function cannot deal with double data without truncating, for example.

Macro directives can be of two types:

#define OBJECT_LIKE_MACRO     followed by a "replacement list" of preprocessor tokens
#define FUNCTION_LIKE_MACRO(with, arguments) followed by a replacement list

What distinguishes these two types of macros is the character that follows the identifier after #define: if it’s an lparen, it is a function-like macro; otherwise, it’s an object-like macro. If the intention is to write a function-like macro, there must not be any white space between the end of the name of the macro and \(. Check this for a detailed explanation.

In C99 or later, you could use static inline int min(int x, int y) { … }.

In C11, you could write a ‘type-generic’ expression for min.

#include <stdio.h>

#define min(x, y) _Generic((x), \
                        long double: min_ld, \
                        unsigned long long: min_ull, \
                        default: min_i \
                        )(x, y)

#define gen_min(suffix, type) \
    static inline type min_##suffix(type x, type y) { return (x < y) ? x : y; }

gen_min(ld, long double)
gen_min(ull, unsigned long long)
gen_min(i, int)

int main(void)
{
    unsigned long long ull1 = 50ULL;
    unsigned long long ull2 = 37ULL;
    printf("min(%llu, %llu) = %llu\n", ull1, ull2, min(ull1, ull2));
    long double ld1 = 3.141592653L;
    long double ld2 = 3.141592652L;
    printf("min(%.10Lf, %.10Lf) = %.10Lf\n", ld1, ld2, min(ld1, ld2));
    int i1 = 3141653;
    int i2 = 3141652;
    printf("min(%d, %d) = %d\n", i1, i2, min(i1, i2));
    return 0;
}

The generic expression could be extended with more types such as double, float, long long, unsigned long, long, unsigned — and appropriate gen_min macro invocations written.

Feedback about page:

Feedback:
Optional: your email if you want me to get back to you:



Table Of Contents