web-dev-qa-db-ja.com

CのMINとMAX

MINMAXはCのどこに定義されていますか?

できる限り総称的かつ安全に入力するために、これらを実装するための最良の方法は何ですか? (主流コンパイラ用のコンパイラ拡張/組み込みが推奨されています。)

247
Matt Joiner

MINMAXはCのどこに定義されていますか?

そうではありません。

可能な限り一般的かつタイプセーフなものとしてこれらを実装するための最良の方法は何ですか(メインストリームコンパイラ用のコンパイラ拡張/組み込みが推奨されます)。

機能として。私は#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))のようなマクロを使用しません、特にあなたがあなたのコードをデプロイすることを計画しているならば。あなた自身のものを書くか、標準の fmax または fmin のようなものを使うか、 GCCのtypeof を使ってマクロを修正してください(あなたもタイプセーフボーナスを得ます):

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

誰もが「ああ、二重評価について知っている、それは問題ない」と言い、数ヶ月後には、何時間もの間最も過酷な問題をデバッグすることになるでしょう。

typeofの代わりに__typeof__を使用していることに注意してください。

ISO Cプログラムにインクルードしたときに動作しなければならないヘッダファイルを書いているなら、typeofの代わりに__typeof__を書いてください。

331
David Titarenco

これは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))

ソースリポジトリは次のとおりです。

84
Mikel

C++にはstd::minstd::maxがありますが、残念ながら、C標準ライブラリには同等のものはありません。次のようなマクロを使って自分でそれらを定義できます。

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

あなたがMAX(++a, ++b)のような何かを書く場合しかし、これは問題を引き起こします。

69
dan04

非標準のコンパイラ拡張を避けて、純粋な標準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には暗黙の型の昇格に関する通常の問題があります。?:operatorは、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))  \
          )

問題は、上記のマクロがintを2つ持つMAX(1, 2)として呼び出されても、_Genericアソシエーションリストのすべての可能なシナリオをマクロ展開しようとすることです。そのため、intには関係ありませんが、ENSURE_floatマクロも拡張されます。そしてそのマクロは意図的に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;
}
20
Lundin

私はそれらが標準化されたマクロだとは思わない。浮動小数点のための標準化された関数、fmaxfmin(そしてfloatのためのfmaxf、そして倍精度のためのfmaxl)がすでにあります。

副作用/二重評価の問題を認識していれば、それらをマクロとして実装することができます。

#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)

ほとんどの場合、コンパイラに任せて、実行しようとしていることを判断し、可能な限り最適化することができます。これがMAX(i++, j++)のように使われるとき問題を引き起こしますが、私は一度にインクリメントされた値の最大値をチェックすることに今までに多くの必要性があることを疑います。最初にインクリメントしてから確認してください。

18
dreamlax

かなり最近の開発のため、これは遅い答えです。 OPは移植性のないGCC(およびclang)拡張typeof - または 'clean' ISO Cの場合は__typeof__に頼る答えを受け入れたので、 gcc-4.9 から利用可能なより良い解決策があります。

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

この拡張の明らかな利点は、__typeof__ソリューションとは異なり、各マクロ引数が一度だけ展開されることです。

__auto_typeはC++ 11のautoの限定形式です。 C++ 11を使用するときにautoの優れた型推論機能を使用しない理由はありませんが、C++コードでは使用できません(または使用すべきではありません)。

そうは言っても、私は 仮定 マクロがextern "C" { ... }スコープに含まれている場合、この構文を使用しても問題はありません。たとえば、Cヘッダーから。私の知る限り、この拡張機能はその方法で検出されていませんinfo clang

17
Brett Hale

私はこれを書いた バージョン 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
11
Matt Joiner

高価な分岐を避けるために最小値/最大値が必要な場合は、3項演算子を使用しないでください。コンパイルするとジャンプします。以下のリンクは、分岐せずに最小/最大関数を実装するための便利な方法を説明しています。

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax

8
cib

minmaxを次のような三次関数で定義すれば、それを指摘する価値があります。

#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)
3
Z boson

私はその男が "C"と言ったことを知っています...しかしもしあなたがチャンスがあれば、C++テンプレートを使います:

template<class T> T min(T a, T b) { return a < b ? a : b; }

タイプセーフで、他のコメントに記載されている++に関する問題はありません。

3
Bas Kuenen

Windef.h(a la #include <windows.h>)はmaxmin(小文字)マクロを持っているように見えますが、それらも "二重評価"の難しさに苦しんでいますが、それらは自分自身を再ロールバックしたくない人のためにあります:)

3
rogerdpack

abの2つの整数の最大値は(int)(0.5((a+b)+abs(a-b)))です。これは、doubleに対して(double)fabs(a-b)を使っても動作します(フロートに対しても同様です)。

0
NRZ

に関連してBrett Haleのコメントclangは2016年頃に__auto_typeのサポートを開始しました( patch を参照)。

0
Lars