Question
In C, where are MIN and MAX defined, if they are defined at all?
What is the best way to implement MIN and MAX as generically and as type-safely as possible? If useful, prefer compiler extensions or built-in features available in mainstream C compilers.
Short Answer
By the end of this page, you will understand that standard C does not provide universal MIN and MAX macros, why naïve macro versions are dangerous, and how to build safer alternatives. You will also see practical approaches using plain macros, static inline functions, C11 _Generic, and GNU-style compiler extensions.
Concept
In C, MIN and MAX are a common need, but they are not part of the core C standard library as universal macros.
You may have seen them in:
- operating-system headers
- third-party libraries
- codebases with custom utility headers
- platform-specific APIs such as Windows headers
That can create confusion, because a program that compiles in one environment may fail in another if it assumes MIN or MAX already exist.
Why this matters
Choosing the smaller or larger of two values sounds simple, but in C the implementation details matter because of:
- multiple evaluation of macro arguments
- type conversions between signed and unsigned values
- lack of true generics in older C versions
- side effects such as
i++being evaluated more than once
A naïve macro like this is common:
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
It looks correct, but it can evaluate a or b more than once. That makes it unsafe for expressions with side effects.
Example:
int i = 3;
int j = 5;
int m = MIN(i++, j);
Depending on the comparison result, i++ may be executed more than once. That is a bug waiting to happen.
The core idea
When implementing MIN and MAX, you usually want some combination of these properties:
- works for many numeric types
- evaluates each argument only once
- preserves type correctness as much as possible
- remains portable enough for your compiler targets
There is no single perfect solution for all C environments. The best choice depends on whether you want:
- maximum portability
- stronger type control
- single evaluation of arguments
- support for mixed numeric types
That trade-off is the real concept behind this question.
Mental Model
Think of MIN and MAX as a machine that compares two boxes and returns one of them.
A bad machine opens the same box multiple times before deciding. If opening a box changes its contents, the result becomes unreliable.
That is what happens with unsafe macros:
MIN(i++, j)is like opening theibox, changing it, then opening it again- the act of checking changes the value being checked
A better machine:
- opens each box exactly once
- labels what type of box it is handling
- compares safely
- returns the correct box without unexpected changes
So the real goal is not just “find the smaller value.” The goal is “find the smaller value without surprising behavior.”
Syntax and Examples
1. Simple macro approach
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
Usage:
int x = 10;
int y = 20;
int small = MIN(x, y);
int large = MAX(x, y);
This is short and portable, but unsafe for expressions with side effects.
2. Safer static inline functions for specific types
static inline int min_int(int a, int b) {
return a < b ? a : b;
}
static inline double max_double(double a, double b) {
return a > b ? a : b;
}
Usage:
int a = 4;
int b = 7;
int m = min_int(a, b);
This avoids multiple evaluation, but each function supports only one type unless you create many versions.
3. C11 _Generic for type-based dispatch
static inline int min_int(int a, int b) { return a < b ? a : b; }
static inline long min_long(long a, long b) { return a < b ? a : b; }
static inline double min_double(double a, double b) { return a < b ? a : b; }
#define min(a, b) _Generic((a) + (b), \
int: min_int, \
long: min_long, \
double: min_double \
)(a, b)
Usage:
int x = 3;
int y = 9;
int m1 = min(x, y);
double p = 2.5;
double q = 1.5;
double m2 = min(p, q);
This is safer and more expressive, but requires C11 and explicit support for the types you want.
4. GNU extension with single evaluation
If you use GCC or Clang in GNU mode, a common pattern is:
#define MIN(a, b) \
({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a < _b ? _a : _b; \
})
#define MAX(a, b) \
({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a > _b ? _a : _b; \
})
Usage:
int i = 5;
int j = 8;
int m = MIN(i++, j);
Here, i++ is evaluated only once because it is first stored in _a.
Recommended beginner rule
- Use simple macros only when arguments are plain variables or constants.
- Use
static inlinefunctions when you want safety and portability for known types. - Use
_Genericwhen you want standard C type-based dispatch. - Use GNU extensions when your project already depends on GCC/Clang features and you want a concise generic solution.
Step by Step Execution
Consider this unsafe macro:
#define MIN(a, b) ((a) < (b) ? (a) : (b))
Now run:
int i = 2;
int j = 5;
int m = MIN(i++, j);
What happens step by step
MIN(i++, j)expands to:
((i++) < (j) ? (i++) : (j))
-
The condition
(i++) < (j)is evaluated.i++produces2- then
ibecomes3 2 < 5is true
-
Because the condition is true, the true branch
(i++)is evaluated.i++produces3- then
ibecomes4
-
So:
mbecomes3- final
ibecomes4
That means i++ happened twice, which is usually not what you intended.
Safer version with GNU extension
#define MIN(a, b) \
({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a < _b ? _a : _b; \
})
Using the same code:
int i = 2;
int j = 5;
int m = MIN(i++, j);
Step by step now
-
_a = (i++)_agets2ibecomes3
-
_b = (j)_bgets5
-
Compare
_a < _b2 < 5is true
-
Return
_ambecomes2
-
Final values:
m == 2i == 3
Now each argument is evaluated only once.
Real World Use Cases
Bounds checking
size_t safe_len = MIN(input_len, buffer_size);
Used when copying data into a buffer to avoid overflow.
Clamping values
MIN and MAX are often combined:
int clamped = MAX(0, MIN(value, 100));
Useful for percentages, scores, volume levels, and UI limits.
Image and graphics code
When computing color channels or pixel boundaries:
int brightness = MIN(raw_brightness, 255);
Parsing and validation
When reading numeric input from files, APIs, or user input:
timeout = MAX(timeout, 1);
This ensures a minimum valid timeout.
Memory and I/O operations
When reading chunks of data:
size_t chunk = MIN(remaining_bytes, block_size);
This is common in file readers, network code, and streaming logic.
Real Codebase Usage
In real projects, developers usually avoid relying on a random global MIN or MAX already existing. Instead, they choose a predictable pattern.
Common patterns
Utility header with project-defined helpers
A codebase may define its own helpers in one shared header:
static inline size_t min_size(size_t a, size_t b) {
return a < b ? a : b;
}
This is explicit and easy to debug.
Guarding macro definitions
If macros are used, teams often avoid conflicts with existing headers:
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
This reduces redefinition issues, though it does not solve the side-effect problem.
Type-specific helpers for important domains
For example, networking code may use size_t, while math code may use double:
static inline size_t min_size(size_t a, size_t b) { return a < b ? a : b; }
static inline double max_double(double a, double b) { return a > b ? a : b; }
Validation and early normalization
Developers often normalize values near the start of a function:
if (count > limit) {
count = limit;
}
This can be clearer than hiding logic inside a macro.
Avoiding mixed signed/unsigned comparisons
Real codebases are careful about this because compiler warnings often reveal bugs:
size_t n = ...;
int limit = ...; /* risky to compare directly */
Instead, code is often rewritten so both operands use the same intended type.
Practical recommendation
In production C code:
- prefer type-specific
static inlinefunctions for portability and safety - use
_Genericif your codebase is modern C11+ - use GNU-style macros only when your project intentionally targets GCC/Clang extensions
Common Mistakes
1. Assuming MIN and MAX are standard C
Broken assumption:
int x = MIN(3, 4); /* may fail if MIN is not defined */
How to avoid it:
- define your own utility
- or use a library/platform header only if you know it is available
2. Using side effects in macro arguments
Broken code:
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int i = 1;
int m = MIN(i++, 10);
Problem:
i++may run more than once
Fix:
- do not pass side-effect expressions to unsafe macros
- or use
static inline,_Generic, or a GNU single-evaluation macro
3. Ignoring signed/unsigned conversions
Broken code:
int a = -1;
unsigned int b = 1;
unsigned int m = MIN(a, b);
Problem:
- usual arithmetic conversions may produce surprising results
-1may be converted to a very large unsigned value
Fix:
- compare values of compatible types
- cast intentionally only when you fully understand the consequences
4. Forgetting parentheses in macros
Broken code:
#define MIN(a, b) a < b ? a : b
Problem:
- operator precedence can change the meaning
Fix:
#define MIN(a, b) ((a) < (b) ? (a) : (b))
5. Expecting one generic function in old C
Broken expectation:
/* C does not have C++-style templates */
Fix:
- use multiple
static inlinefunctions - or use C11
_Generic - or compiler extensions if your environment allows them
6. Returning the wrong type accidentally
If you mix float, double, int, and unsigned types, the result type may not be what you intuitively expect.
How to avoid it:
- keep both arguments the same type where possible
- use explicit type-specific helpers for important code paths
Comparisons
| Approach | Portable | Single evaluation | Type-safe | Generic | Notes |
|---|---|---|---|---|---|
| Simple macro | Yes | No | Weak | Yes | Short, but unsafe with side effects |
static inline function | Yes | Yes | Strong for one type | No | Best simple portable option |
C11 _Generic + inline functions | C11+ | Yes | Good | Limited by listed types | Standard way to emulate generics |
| GNU statement-expression macro | GCC/Clang GNU mode | Yes | Better | Yes | Practical, but non-standard |
Simple macro vs inline function
| Feature | Macro | static inline |
|---|---|---|
| Works for many types | Yes | No, one function per type |
| Evaluates once | Usually no | Yes |
| Debugging | Harder | Easier |
| Compiler errors | Can be confusing | Usually clearer |
_Generic vs GNU extensions
| Feature | _Generic | GNU extension |
|---|---|---|
| Standard C | Yes, C11+ | No |
| Single evaluation | Yes, with inline functions | Yes |
| Easy to write | Moderate | Easy if extensions are allowed |
| Compiler support | Modern C compilers | GCC and Clang in GNU mode |
Best choice by situation
- Need maximum portability: use
static inlinefunctions - Need standard generic behavior in modern C: use
_Generic - Need concise generic code on GCC/Clang: use GNU extensions
- Need a quick utility for constants and variables only: a simple macro may be acceptable
Cheat Sheet
Key fact
Standard C does not define universal MIN and MAX macros.
Unsafe but common
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
- portable
- generic
- may evaluate arguments more than once
Safe for one type
static inline int min_int(int a, int b) {
return a < b ? a : b;
}
- portable
- evaluates once
- one type per function
Standard generic option in C11+
#define min(a, b) _Generic((a) + (b), \
int: min_int, \
double: min_double \
)(a, b)
- type-based dispatch
- explicit supported types
GNU extension option
#define MIN(a, b) \
({ __auto_type _a = (a); __auto_type _b = (b); _a < _b ? _a : _b; })
- evaluates once
- convenient
- not standard C
Rules to remember
- Do not assume
MINorMAXalready exist. - Avoid side effects in plain macro arguments.
- Prefer matching operand types.
- Be careful with signed vs unsigned comparisons.
- Add parentheses around macro parameters and the whole expression.
FAQ
Are MIN and MAX part of the C standard library?
No. Standard C does not provide universal MIN and MAX macros for general use.
Why is the usual #define MIN(a,b) macro unsafe?
Because macro arguments can be evaluated more than once. Expressions like i++ can therefore produce unexpected results.
What is the safest portable way to implement MIN and MAX in C?
Type-specific static inline functions are usually the safest and most portable practical option.
How can I make MIN and MAX generic in standard C?
Use C11 _Generic together with type-specific static inline functions.
Is there a one-line generic solution for GCC or Clang?
Yes. GNU statement expressions with __auto_type are a common approach, but they are compiler extensions, not standard C.
Can I use MIN and MAX with different numeric types?
You can, but mixed types may trigger implicit conversions, especially signed/unsigned conversions, which can cause surprising behavior.
Should I use macros or functions in production code?
Prefer functions or _Generic-based wrappers when safety and clarity matter. Use macros only when you understand their trade-offs.
Mini Project
Description
Create a small numeric utility header for C that provides safe minimum and maximum operations for common types. This project demonstrates the trade-offs between portability, safety, and generic behavior, and mirrors what real codebases often do in shared utility headers.
Goal
Build a reusable min/max utility that evaluates arguments once and works for several common numeric types.
Requirements
- Create type-specific
static inlinefunctions for at leastint,long, anddouble. - Add C11
_Genericmacros namedminandmaxthat dispatch to the correct function. - Write a
mainfunction that tests each supported type. - Include at least one example that would be unsafe with a plain macro, such as
i++. - Print the results so the behavior can be verified easily.
Keep learning
Related questions
2D Array Loop Order and Cache Performance in C
Learn why swapping nested loops changes 2D array performance in C, using row-major memory layout, cache locality, and practical benchmarks.
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.