あるとしても、Cで定義されてMIN
いるのはどこですか?MAX
これらをできるだけ一般的に実装し、可能な限り安全に入力するための最良の方法は何ですか?(主流のコンパイラー用のコンパイラー拡張機能/組み込みが望ましい。)
あるとしても、Cで定義されてMIN
いるのはどこですか?MAX
これらをできるだけ一般的に実装し、可能な限り安全に入力するための最良の方法は何ですか?(主流のコンパイラー用のコンパイラー拡張機能/組み込みが望ましい。)
あるとしても、Cで定義されて
MIN
いるのはどこですか?MAX
そうではありません。
これらを可能な限り一般的かつタイプセーフに実装するための最良の方法は何ですか(主流コンパイラー用のコンパイラー拡張機能/組み込みが望ましい)。
関数として。#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
特にコードをデプロイする予定がある場合は、のようなマクロは使用しません。独自に作成するか、standardfmax
またはのようなものを使用するか、 GCCステートメント式でGCCのtypeof(タイプセーフティボーナスも取得します)をfmin
使用してマクロを修正します。
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
誰もが「二重評価について知っている、それは問題ない」と言っており、数か月後には、最もひどい問題を何時間もデバッグすることになります。
__typeof__
の代わりにの使用に注意してくださいtypeof
:
ISO Cプログラムにインクルードするときに機能する必要のあるヘッダーファイルを作成する場合は、
__typeof__
の代わりに 作成してtypeof
ください。
また、GNU libc(Linux)およびFreeBSDバージョンsys/param.h
ので提供されており、dreamlaxによって提供される定義があります。
Debianの場合:
$ uname -sr
Linux 2.6.11
$ cat /etc/debian_version
5.0.2
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.
FreeBSDの場合:
$ uname -sr
FreeBSD 5.5-STABLE
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
ソースリポジトリは次のとおりです。
std::min
C ++にはとがありstd::max
ますが、AFAIKでは、C標準ライブラリに同等のものはありません。次のようなマクロを使用して自分で定義できます
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
しかし、のようなものを書くと、これは問題を引き起こしますMAX(++a, ++b)
。
非標準のコンパイラ拡張を避け、純粋な標準C(ISO 9899:2011)で完全にタイプセーフなマクロとして実装します。
解決
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
使用法
MAX(int, 2, 3)
説明
マクロMAXは、パラメーターに基づいて別のマクロを作成しtype
ます。この制御マクロは、特定のタイプに実装されている場合、両方のパラメーターが正しいタイプであることを確認するために使用されます。type
がサポートされていない場合、コンパイラエラーが発生します。
xまたはyのいずれかが正しいタイプでない場合、ENSURE_
マクロでコンパイラエラーが発生します。より多くのタイプがサポートされている場合は、このようなマクロをさらに追加できます。算術型(整数、浮動小数点数、ポインターなど)のみが使用され、構造体や配列などは使用されないと想定しました。
すべてのタイプが正しい場合、GENERIC_MAXマクロが呼び出されます。Cマクロを作成する際の通常の標準的な予防措置として、各マクロパラメータの前後に追加の括弧が必要です。
次に、Cの暗黙的な型昇格には通常の問題があります。?:
演算子は、2番目と3番目のオペランドのバランスを取ります。たとえば、の結果はGENERIC_MAX(my_char1, my_char2)
になりますint
。マクロがそのような潜在的に危険な型昇格を行うのを防ぐために、意図された型にキャストされた最終的な型が使用されました。
理論的根拠
マクロの両方のパラメーターを同じタイプにする必要があります。?:
それらの1つが異なる型である場合、のような演算子は暗黙の型昇格を生成するため、マクロは型安全ではなくなります。また、そうなるため、上記で説明したように、常に最終結果を目的のタイプにキャストする必要があります。
パラメータが1つしかないマクロは、はるかに簡単な方法で記述できたはずです。ただし、2つ以上のパラメーターを使用する場合は、追加のタイプパラメーターを含める必要があります。このようなことは残念ながら不可能なので:
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
問題は、上記のマクロがMAX(1, 2)
2つのように呼び出された場合でも、関連付けリストint
のすべての可能なシナリオをマクロ展開しようとすることです。_Generic
したがって、ENSURE_float
マクロはに関連していなくても、マクロも展開されますint
。また、そのマクロには意図的にfloat
型のみが含まれているため、コードはコンパイルされません。
これを解決するために、代わりにプリプロセッサフェーズで##演算子を使用してマクロ名を作成し、マクロが誤って展開されないようにしました。
例
#include <stdio.h>
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
int main (void)
{
int ia = 1, ib = 2;
float fa = 3.0f, fb = 4.0f;
double da = 5.0, db = 6.0;
printf("%d\n", MAX(int, ia, ib)); // ok
printf("%f\n", MAX(float, fa, fb)); // ok
//printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong
//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
return 0;
}
かなり最近の開発のため、これは遅い答えです。typeof
OPは、移植性のないGCC(およびclang)拡張機能(または__typeof__
「クリーンな」ISO C )に依存する回答を受け入れたため、 gcc-4.9の時点で利用可能なより優れたソリューションがあります。
#define max(x,y) ( \
{ __auto_type __x = (x); __auto_type __y = (y); \
__x > __y ? __x : __y; })
この拡張機能の明らかな利点は、__typeof__
ソリューションとは異なり、各マクロ引数が1回だけ展開されることです。
__auto_type
C++11の限定された形式ですauto
。auto
C ++ 11を使用する場合の優れた型推論機能を使用しない理由はありませんが、C ++コードでは使用できません(または使用しないでください) 。
そうは言っても、マクロextern "C" { ... }
がスコープに含まれている場合は、この構文を使用しても問題はないと思います。たとえば、Cヘッダーから。AFAIK、この拡張機能はその方法を見つけていませんinfo clang
標準化されたマクロではないと思います。fmax
浮動小数点用に、およびfmin
(および浮動小数点fmaxf
用、およびfmaxl
ロングダブル用に)標準化された関数がすでにあります。
副作用/二重評価の問題を認識している限り、それらをマクロとして実装できます。
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
ほとんどの場合、コンパイラに任せて、何をしようとしているのかを判断し、可能な限り最適化することができます。これを使用すると問題が発生しMAX(i++, j++)
ますが、増分値の最大値を一度にチェックする必要があるとは思えません。最初にインクリメントしてからチェックします。
@David Titarencoはここに釘付けにしましたが、少なくとも少しきれいにして見栄えを良くし、両方min()
を max()
一緒に表示して、ここからのコピーと貼り付けを簡単にします。:)
2020年4月25日更新:セクション3を追加して、CとC ++の両方を学習したり、一方から他方に移行したりする人にとって貴重な比較として、C++テンプレートでもこれがどのように行われるかを示します。私は、この回答を何度も何度も繰り返すことができる標準的な参照にするために、徹底的かつ事実に基づいて正確になるように最善を尽くしました。私と同じように役立つことを願っています。
この手法は一般的に使用されており、適切な使用方法、「事実上の」方法を知っている人から高く評価されており、適切に使用すれば問題なく使用できますが、バグがある場合(二重評価の副作用と考えてください)。変数の割り当てを含む式を渡して比較します。
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
この手法は、上記の「二重評価」の副作用とバグを回避するため、これを行うための優れた、より安全な、「より現代的な」GCCCの方法と見なされます。clangは設計上gcc互換であるため、gccコンパイラとclangコンパイラの両方で動作することを期待してください(この回答の下部にあるclangノートを参照してください)。
ただし、ステートメント式は明らかにインライン化されているため、独自のローカル変数スコープを持たないため、 「変数シャドウイング」効果には注意してください。
#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; \
})
gccステートメント式では、コードブロックの最後の式は、関数から返されたかのように、式から「返される」ものであることに注意してください。GCCのドキュメントには、次のように記載されています。
複合ステートメントの最後は、式の後にセミコロンを付ける必要があります。この部分式の値は、構成全体の値として機能します。(中括弧内で最後に他の種類のステートメントを使用する場合、構成はvoid型であるため、事実上値がありません。)
C ++注:C ++を使用する場合は、代わりにこのタイプの構成にテンプレートを使用することをお勧めしますが、個人的にはテンプレートが嫌いで、組み込みC ++でもCスタイルを頻繁に使用し、好むため、C++では上記の構成のいずれかを使用します。
このセクションは2020年4月25日に追加されました。
私は過去数か月間、大量のC ++を実行してきましたが、C ++コミュニティでは、可能であれば、マクロよりもテンプレートを優先するというプレッシャーが非常に強くなっています。その結果、私はテンプレートの使用が上手くなり、完全を期すためにここにC ++テンプレートのバージョンを入れて、これをより標準的で徹底的な答えにしたいと思っています。
C++の基本的な関数テンプレートバージョンは次のようにmax()
なりmin()
ます。
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;
}
ここでC++テンプレートについてさらに読んでください:ウィキペディア:テンプレート(C ++)。
ただし、max()
とは両方とも、ヘッダー( )min()
のC++標準ライブラリの一部です。C ++標準ライブラリでは、上記とは少し異なる方法で定義されています。たとえば、C ++ 14のデフォルトのプロトタイプは、すぐ上のcplusplus.comリンクでプロトタイプを確認すると次のようになります。<algorithm>
#include <algorithm>
std::max<>()
std::min<>()
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);
キーワードtypename
はのエイリアスであることに注意してくださいclass
(つまり、C ++テンプレートの発明後に後で認識されたため、テンプレートタイプは、のみではなく通常のタイプ(、など)である可能性があります)<typename T>
。クラスタイプ。<class T>
int
float
ここでは、入力型と戻り型の両方がであることがわかりますconst T&
。これは、「型への定数参照」を意味しT
ます。これは、入力パラメーターと戻り値が、値ではなく参照によって渡されることを意味します。これはポインタの受け渡しに似ており、クラスオブジェクトなどの大きな型に対してより効率的です。関数のconstexpr
一部は関数自体を変更し、関数がコンパイル時に評価できる必要があることを示します(少なくともconstexpr
入力パラメーターが提供されている場合)が、コンパイル時に評価できない場合は、デフォルトで他の通常の関数と同様に、実行時の評価。
C ++関数のコンパイル時の側面により、constexpr
Cマクロのようなものになります。関数のコンパイル時の評価が可能な場合は、またはマクロの置換constexpr
と同じように、コンパイル時に実行されます。CまたはC++でもコンパイル時に完全に評価されます。このC++テンプレート情報の追加のリファレンスについては、以下を参照してください。MIN()
MAX()
ウィキペディアからのClangノート:
[Clang]は、GNUコンパイラコレクション(GCC)のドロップイン置換として機能するように設計されており、ほとんどのコンパイルフラグと非公式の言語拡張をサポートしています。
このバージョンは、MSVC、GCC、C、およびC++で動作するように作成しました。
#if defined(__cplusplus) && !defined(__GNUC__)
# include <algorithm>
# define MIN std::min
# define MAX std::max
//# define TMIN(T, a, b) std::min<T>(a, b)
//# define TMAX(T, a, b) std::max<T>(a, b)
#else
# define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
({ \
decltype(lexpr) lvar = (lexpr); \
decltype(rexpr) rvar = (rexpr); \
lvar binoper rvar ? lvar : rvar; \
})
# define _CHOOSE_VAR2(prefix, unique) prefix##unique
# define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
# define _CHOOSE(binoper, lexpr, rexpr) \
_CHOOSE2( \
binoper, \
lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
)
# define MIN(a, b) _CHOOSE(<, a, b)
# define MAX(a, b) _CHOOSE(>, a, b)
#endif
高価な分岐を回避するために最小/最大が必要な場合は、ジャンプにコンパイルされるため、三項演算子を使用しないでください。以下のリンクは、分岐せずに最小/最大関数を実装するための便利な方法を説明しています。
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
指摘する価値があると思います。定義するmin
と、次max
のような三項演算を使用できます。
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
次に、の特殊なケースで同じ結果を得るには、引数を交換する必要がありfmin(-0.0,0.0)
ますfmax(-0.0,0.0)
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
Windef.h
(a la #include <windows.h>
)にはマクロmax
と(小文字の)マクロがあり、これも「二重評価」の難しさに悩まされているように見えmin
ますが、自分で再ロールしたくない人のためにあります:)
私はその男が「C」と言ったことを知っています...しかし、機会があれば、C++テンプレートを使用してください。
template<class T> T min(T a, T b) { return a < b ? a : b; }
安全に入力してください。他のコメントで言及されている++に問題はありません。
<?, >?, <?=, >?=
非常に古いバージョンのGCCには、演算子がありました<?, >?
(ここを参照、ここではC ++でしたが、当時はC拡張としても適用されていたと思います)<?=, >?=
割り当てステートメントに対応する演算子も見ました。
オペランドは一度評価され、非常に短い代入ステートメントでも許可されました。一般的な最小/最大割り当てと比較して非常に短いです。これを超えることができるものは何もありません。
それらは次の省略形でした:
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;
最小値を見つけることは非常に簡潔です:
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;
}
これらのオペレーターは素晴らしいと思うので、これがいつかGCCに戻されることを願っています。
最大2つの整数a
であり、b
です(int)(0.5((a+b)+abs(a-b)))
。これは、ダブルスでも機能する可能性があります(double)
(fabs(a-b)
フロートの場合と同様)
最も簡単な方法は、ファイル内のグローバル関数として定義し.h
、プログラムが多数のファイルでモジュール化されている場合は、いつでも呼び出すことです。そうでない場合double MIN(a,b){return (a<b?a:b)}
は、最も簡単な方法です。