Where are MIN and MAX defined in C, if at all?
What is the best way to implement these, as generically and type safely as possible? (Compiler extensions/builtins for mainstream compilers preferred.)
Where are MIN and MAX defined in C, if at all?
What is the best way to implement these, as generically and type safely as possible? (Compiler extensions/builtins for mainstream compilers preferred.)
@David Titarenco nailed it here, but let me at least clean it up a bit to make it look nice, and show both min() and max() together to make copying and pasting from here easier. :)
Update 25 Apr. 2020: I've also added a Section 3 to show how this would be done with C++ templates too, as a valuable comparison for those learning both C and C++, or transitioning from one to the other. I've done my best to be thorough and factual and correct to make this answer a canonical reference I can come back to again and again, and I hope you find it as useful as I do.
This technique is commonly used, well-respected by those who know how to use it properly, the "de facto" way of doing things, and fine to use if used properly, but buggy (think: double-evaluation side effect) if you ever pass expressions including variable assignment in to compare:
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
This technique avoids the above "double-evaluation" side effects and bugs, and is therefore considered the superior, safer, and "more modern" GCC C way to do this. Expect it to work with both the gcc and clang compilers, since clang is, by design, gcc-compatible (see the clang note at the bottom of this answer).
BUT: DO watch out for "variable shadowing" effects still, as statement expressions are apparently inlined and therefore do NOT have their own local variable scope!
#define max(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; \
})
#define min(a,b) \
({ \
__typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a < _b ? _a : _b; \
})
Note that in gcc statement expressions, the last expression in the code block is what is "returned" from the expression, as though it was returned from a function. GCC's documentation says it this way:
The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. (If you use some other kind of statement last within the braces, the construct has type void, and thus effectively no value.)
C++ Note: if using C++, templates are probably recommended for this type of construct instead, but I personally dislike templates and would probably use one of the above constructs in C++ anyway, as I frequently use and prefer C styles in embedded C++ as well.
This section added 25 Apr. 2020:
I've been doing a ton of C++ the past few months, and the pressure to prefer templates over macros, where able, in the C++ community is quite strong. As a result, I've been getting better at using templates, and want to put in the C++ template versions here for completeness and to make this a more canonical and thorough answer.
Here's what basic function template versions of max() and min() might look like in C++:
template <typename T>
T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T>
T min(T a, T b)
{
return a < b ? a : b;
}
Do additional reading about C++ templates here: Wikipedia: Template (C++).
However, both max() and min() are already part of the C++ standard library, in the <algorithm> header (#include <algorithm>). In the C++ standard library they are defined slightly differently than I have them above. The default prototypes for std::max<>() and std::min<>(), for instance, in C++14, looking at their prototypes in the cplusplus.com links just above, are:
template <class T>
constexpr const T& max(const T& a, const T& b);
template <class T>
constexpr const T& min(const T& a, const T& b);
Note that the keyword typename is an alias to class (so their usage is identical whether you say <typename T> or <class T>), since it was later acknowledged after the invention of C++ templates, that the template type might be a regular type (int, float, etc.) instead of only a class type.
Here you can see that both of the input types, as well as the return type, are const T&, which means "constant reference to type T". This means the input parameters and return value are passed by reference instead of passed by value. This is like passing by pointers, and is more efficient for large types, such as class objects. The constexpr part of the function modifies the function itself and indicates that the function must be capable of being evaluated at compile-time (at least if provided constexpr input parameters), but if it cannot be evaluated at compile-time, then it defaults back to a run-time evaluation, like any other normal function.
The compile-time aspect of a constexpr C++ function makes it kind-of C-macro-like, in that if compile-time evaluation is possible for a constexpr function, it will be done at compile-time, same as a MIN() or MAX() macro substitution could possibly be fully evaluated at compile-time in C or C++ too. For additional references for this C++ template info, see below.
std::max()If using C++, I'd like to add that the built-in std::max() function in the <algorithm> header file has a variety of forms. See the "Possible implementation" section on the documentation page at the cppreference.com community wiki (https://en.cppreference.com/w/cpp/algorithm/max) for 4 possible implementations for the 4 forms of std::max().
Normal usages include:
std::max(100, 200);
...but if you'd like to compare many numbers at once, you can use the 4th form, which accepts a std::initializer_list<T>, like this:
Function declaration:
template< class T, class Compare >
constexpr T max( std::initializer_list<T> ilist, Compare comp );
Usage:
// Compare **3 or more numbers** by passing a curly-brace-initialized
// `std::initializer_list<>` to `std::max()`!:
std::max({100, 200, 300}); // result is 300
std::max({100, 200, 300, 400}); // result is 400
std::max({100, 200, 300, 400, 500}); // result is 500
std::max({100, 200, 300, 400, 500, 600}); // result is 600
// etc.
Clang note from Wikipedia:
[Clang] is designed to act as a drop-in replacement for the GNU Compiler Collection (GCC), supporting most of its compilation flags and unofficial language extensions.
<?, >?, <?=, >?=In a very old version of GCC there were the operators <?, >? (see here, here it was in C++ but I think it also applied as a C extension back then)
I have also seen the operators <?=, >?= corresponding to the assignment statements.
The operands were evaluated once and even allowed for a very short assignment statement. Its very short compared to common min/max assignments. There is nothing that can top this.
Those were a shorthand for the following:
min(a, b) === a < b ? a : b === a <? b;
max(a, b) === a > b ? a : b === a >? b;
a = min(a, b); === if(b < a) a = b; === a <?= b;
a = max(a, b); === if(b > a) a = b; === a >?= b;
Finding the minimum is very concise:
int find_min(const int* ints, int num_ints)
{
assert(num_ints > 0);
int min = ints[0];
for(int i = 1; i < num_ints; ++i)
min <?= ints[i];
return min;
}
I hope this might be some day brought back to GCC, because I think these operators are genious.
I'd avoid defining MIN and MAX as preprocessor macros all together. Even with the improved implementation that avoids double evaluation, i.e.
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
You get other issues such as compiler errors when you try to use this outside of functions (see here for an example) or variable shadowing as referenced in this answer.
If you desperately need the performance advantage that goes with macros, i.e. code inlining, implement them as inline functions. Most modern compilers would honor the inline. I.e. something like this
inline int32_t MAX(int32_t a, int32_t b) { return((a) > (b) ? a : b); }
inline int32_t MIN(int32_t a, int32_t b) { return((a) < (b) ? a : b); }
You'd have to implement one variant per data type you need but usually you'd be working with a limited set of data types. Having said that bear in mind that comparing floating points has it's own quirks and you'd need more sophisticated code than the above for these (see here for a discussion).