回答:
どこに
MIN
ありMAX
、Cで定義されているとすれば、それはすべてですか?
そうではありません。
これらを実装するための最良の方法は何ですか?可能な限り一般的でタイプセーフです(主流のコンパイラー向けのコンパイラー拡張/ビルトインが望ましい)。
関数として。#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
特にコードをデプロイする場合は、のようなマクロは使用しません。標準のようなもの、独自の、使用を書くのいずれかfmax
またはfmin
使用してマクロを修正、またはGCCのtypeof演算では(あなたもtypesafetyのボーナスを得る)GCC文の表現を:
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
誰もが「ああ、二重評価について知っている、それは問題ない」と言って、数か月後、あなたは何時間にもわたって最も厄介な問題をデバッグするでしょう。
の__typeof__
代わりにを使用していることに注意してくださいtypeof
。
ISO Cプログラムにインクルードするときに機能する必要があるヘッダーファイルを作成する場合は、の
__typeof__
代わりに書き込みますtypeof
。
decltype
キーワードをいじくり回すことです-それでも、Visual Studioはマクロで複合ステートメントを実行できません(decltype
とにかくC ++です)、つまりGCCの({ ... })
構文なので、とにかくそれが不可能であると確信しています。私はこの問題に関して他のコンパイラーを見たことがありません。申し訳ありませんが、Luther:S
MAX(someUpperBound, someRandomFunction())
ランダムな値をある上限に制限しようとしたケースを見たことがあります。それはひどい考えでしたが、MAX
彼が使用していたので二重評価の問題があったため、それもうまくいきませんでした。
MIN(x++, y++)
、プリプロセッサを呼び出すと、次のコードが生成されます(((x++) < (y++)) ? (x++) : (y++))
。だから、x
およびy
2回インクリメントされることになり。
これは、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))
ソースリポジトリは次のとおりです。
openSUSE/Linux 3.1.0-1.2-desktop
/ gcc version 4.6.2 (SUSE Linux)
すぎ。:)可搬性が悪い。
ありますstd::min
し、std::max
C ++で、しかし、私の知る限りでは、C標準ライブラリには同等ありません。次のようなマクロを使用して自分で定義できます
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
しかし、これはあなたがのようなものを書くと問題を引き起こしますMAX(++a, ++b)
。
#define MULT(x, y) x * y
。次に、にMULT(a + b, a + b)
展開されa + b * a + b
、a + (b * 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つが異なるタイプである場合、演算子like ?:
は暗黙的なタイプの昇格を生成するため、マクロはタイプセーフではなくなります。また、そのため、上記で説明したように、常に最終結果を目的の型にキャストする必要があります。
パラメータが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;
}
GENERIC_MAX
ちなみに、そのマクロは悪い考えです。GENERIC_MAX(var++, 7)
理由を探る必要があるだけです:-)今日(特に、大幅に最適化/インライン化されたコンパイラの場合)、マクロはほとんど単純な形式だけに委ねられるべきです。関数のようなものは関数としてより良く、値のグループのものは列挙としてより良いです。
標準化されたマクロだとは思いません。浮動小数点用の標準化された関数がすでにfmax
ありますfmin
(そして浮動小数点数のためにfmaxf
、そしてfmaxl
長います。
副作用/二重評価の問題を認識している限り、それらをマクロとして実装できます。
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
ほとんどの場合、コンパイラーに任せて、何をしようとしているかを判断し、できる限り最適化します。これをのように使用すると問題が発生しMAX(i++, j++)
ますが、インクリメントされた値の最大値を一度に確認する必要性はこれまでにないでしょう。最初にインクリメントしてからチェックします。
これはかなり最近の開発による遅い答えです。OPは、移植性のないGCC(およびclang)拡張に依存する回答を受け入れたため、typeof
または__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
。C ++コードでは使用できません(使用しないでください)。ただし、auto
C ++ 11 を使用する場合の優れた型推論機能を使用しない理由はありません。
とはいえ、マクロがスコープに含まれている場合は、この構文を使用しても問題は発生しないと思いextern "C" { ... }
ます。たとえば、Cヘッダーから。私の知る限り、この拡張機能はその方法を見つけることができませんでしたinfo clang
c-preprocessor
タグがありますが、同意します。gccの__always_inline__
属性のようなものを使用しない限り、関数は上記のキーワードを使用してもインライン化されるとは限りません。
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
高価な分岐を回避するために最小/最大が必要な場合は、3項演算子を使用しないでください。3項演算子は、コンパイルしてジャンプするためです。以下のリンクは、分岐なしで最小/最大関数を実装するための便利な方法を説明しています。
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
@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))
この手法は、上記の「二重評価」の副作用とバグを回避するため、優れた、より安全な、「より現代的な」GCCと見なされています。 Cの方法れています。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 ++で上記の構成の1つを使用します。
このセクションは、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 ++標準ライブラリの<algorithm>
ヘッダー(#include <algorithm>
)に含まれています。C ++標準ライブラリでは、上記のライブラリとは少し異なる方法で定義されています。デフォルトのプロトタイプstd::max<>()
とはstd::min<>()
、例えば、C ++ 14で、ちょうど上記cplusplus.comリンクでの試作品を見て、以下のとおりです。
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
(その使用量は、あなたが言うか同じであるので、<typename T>
または<class T>
それは後で、テンプレートの種類は(レギュラータイプかもしれないと、C ++テンプレートの発明の後に認められたことから、) int
、float
など、)の代わりのみのクラスタイプ。
ここでは、入力タイプと戻り値タイプの両方がであることがわかりますconst T&
。これは、「タイプへの定数参照」を意味しT
ます。つまり、入力パラメーターと戻り値は、値渡しではなく参照渡しになります。これはポインタによる受け渡しに似ており、クラスオブジェクトなどの大きな型の場合により効率的です。constexpr
関数の一部は関数自体を変更し、関数がコンパイル時に評価できる必要があることを示します(少なくともconstexpr
入力パラメーターが提供されている場合)。ただし、コンパイル時に評価できない場合、デフォルトでaに戻ります。他の通常の関数と同様に、ランタイム評価。
constexpr
C ++関数のコンパイル時の側面は、Cマクロのようなものです。つまり、constexpr
関数のコンパイル時の評価が可能な場合、それは、MIN()
またはMAX()
マクロの置換が可能であるのと同じように、コンパイル時に行われます。CまたはC ++でもコンパイル時に完全に評価されます。このC ++テンプレート情報のその他のリファレンスについては、以下を参照してください。
ウィキペディアの Clangのメモ:
[Clang]は、GNU Compiler Collection(GCC)のドロップイン代替として機能するように設計されており、そのコンパイルフラグと非公式言語拡張のほとんどをサポートしています。
それは私はあなたが定義した場合だと思う指摘価値がある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)
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
男が「C」と言ったのは知っていますが、機会があればC ++テンプレートを使用してください。
template<class T> T min(T a, T b) { return a < b ? a : b; }
タイプセーフで、他のコメントに記載されている++に問題はありません。
二つの整数の最大値a
とがb
あります(int)(0.5((a+b)+abs(a-b)))
。これはまたでも動作(double)
し、fabs(a-b)
ダブルスのために(フロートについても同様)
最も簡単な方法は.h
、プログラムをファイル内でグローバル関数として定義し、プログラムが多数のファイルでモジュール化されている場合は、必要なときにいつでも呼び出すことです。そうでない場合double MIN(a,b){return (a<b?a:b)}
は、最も簡単な方法です。
warning: expression with side-effects multiply evaluated by macro
使用時...