Getting started
Function pointers
Operators
Data types
Arrays
Undefined behavior
Random numbers
Preprocessor and macros
Signal handling
Variable arguments
Files and I/O streams
Assertion
Linked lists
Generic selection
X-macros
Function parameters
Pointers
Structs
Sequence points
Command-line arguments
Aliasing and effective type
Compilation
Identifier scope
Bit-fields
Strings
Comman pitfalls
Common pitfalls
Mixing signed and unsigned integers in arithmetic operations
Macros are simple string replacements
Forgetting to copy the return value of realloc into a temporary
Forgetting to allocate one extra byte for 0
Misunderstanding array decay
Forgetting to free memory memory leaks
Copying too much
Mistakenly writing instead of when comparing
Incautious use of semicolons
Adding a semicolon to a define
Newline character is not consumed in typical scanf call
Checking logical expression against true
Undefined reference errors when linking
Doing extra scaling in pointer arithmetic
Multi-line comments cannot be nested
Comparing floating point numbers
Ignoring return values of library functions
Floating point literals are of type double by default
Recursive function missing out the base condition
Using character constants instead of string literals and vice versa
Overstepping array boundaries
Passing unadjacent arrays to functions expecting real multidimensional arrays
Error handling
Implicit and explicit conversions
Type qualifiers
Valgrind
Typedef
Selection statements
Declaration vs. definitio
Standard math
Boolean
Literals for numbers, characters and strings
Storage classes
Declarations
Formatted Input/Output
Compound literals
Inline assembly
Threads native
Initialization
Structure padding and packing
Memory management
Implementation-defined behaviour
Atomics
Iteration statements, loops, for, while, do-while
Enumerations
Jump Statements
Create and include header files
Testing frameworks
ctype.h characters classification
Pass 2D-arrays to functions
Side effects
Multi-character character sequences
Constrains
Inlining
Unions
Multi-threading
Common idioms and developer practices
Inter-process communication (IPC)
Comments
Contributors

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