CのMINおよびMAX


300

どこにMINありMAX、Cで定義されているとすれば、それはすべてですか?

これらをできるだけ一般的に入力安全に実装するための最良の方法は何ですか?(主流のコンパイラー向けのコンパイラー拡張/ビルトインが望ましい。)

回答:


392

どこに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


68
ご存知のとおり、gccに次のような警告があった場合は非常に便利です:warning: expression with side-effects multiply evaluated by macro使用時...
caf

23
@caf:プリプロセッサがC構文のより複雑な知識を持っている必要はありませんか?
dreamlax 2010

3
多くのことを理解しようとした後、VC ++でこれを行う方法はないと思いますが、最善の方法は、MSVC ++ 2010の新しいdecltypeキーワードをいじくり回すことです-それでも、Visual Studioはマクロで複合ステートメントを実行できません(decltypeとにかくC ++です)、つまりGCCの({ ... })構文なので、とにかくそれが不可能であると確信しています。私はこの問題に関して他のコンパイラーを見たことがありません。申し訳ありませんが、Luther:S
David Titarenco 2010

7
@dreamlax私はかつて誰かがMAX(someUpperBound, someRandomFunction())ランダムな値をある上限に制限しようとしたケースを見たことがあります。それはひどい考えでしたが、MAX彼が使用していたので二重評価の問題があったため、それもうまくいきませんでした。
Zev Eisenberg

8
@SoumenたとえばMIN(x++, y++)、プリプロセッサを呼び出すと、次のコードが生成されます(((x++) < (y++)) ? (x++) : (y++))。だから、xおよびy2回インクリメントされることになり。
アントニオ

91

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

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


上記の私の回答で、アクセスできるシステムの定義を追加しました(コメントフィールドは、私の知る限り、フォーマットを受け入れません)。FreeBSD / Linux / glibcソースリポジトリへのリンクを見つけようとします。
ミケル

+1。非常に素晴らしい。以下のための作品openSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) すぎ。:)可搬性が悪い。
ジャック

Cygwinでも動作します。
CMCDragonkai 2017年

1
ちょっと待って。二重評価は防げませんか?:3
user1857492 2017年

76

ありますstd::minし、std::maxC ++で、しかし、私の知る限りでは、C標準ライブラリには同等ありません。次のようなマクロを使用して自分で定義できます

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

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


10
なぜブラケットを入れすぎるのですか?彼ら#define MIN(A, B) ((A < B) ? A : B)が柔軟な方法ではないと言ったクイズを見つけました、なぜですか???

78
@Makouda:マクロの余分な括弧は、演算子の優先順位の問題を回避するのに役立ちます。たとえば、を検討してください#define MULT(x, y) x * y。次に、にMULT(a + b, a + b)展開されa + b * a + ba + (b * a) + b優先順位が原因で解析されます。それはおそらくプログラマが意図したものではありません。
dan04 2014年

?:とにかく優先順位が最も低い場合は不要
Winger Sendon

@WingerSendon:ありません。コンマ演算子にはあります。
dan04

24

非標準のコンパイラー拡張を避け、完全にタイプセーフなマクロとして純粋な標準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)理由を探る必要があるだけです:-)今日(特に、大幅に最適化/インライン化されたコンパイラの場合)、マクロはほとんど単純な形式だけに委ねられるべきです。関数のようなものは関数としてより良く、値のグループのものは列挙としてより良いです。
paxdiablo

21

標準化されたマクロだとは思いません。浮動小数点用の標準化された関数がすでにfmaxありますfmin(そして浮動小数点数のためにfmaxf、そしてfmaxl長います。

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

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

ほとんどの場合、コンパイラーに任せて、何をしようとしているかを判断し、できる限り最適化します。これをのように使用すると問題が発生しMAX(i++, j++)ますが、インクリメントされた値の最大値を一度に確認する必要性はこれまでにないでしょう。最初にインクリメントしてからチェックします。


数学ライブラリには明らかにmin関数とmax関数があるので、これが推奨される答えです。 。cplusplus.com reference
cmath

@imranalあなたは正確に何を話しているのですか?のそれらのライブラリ実装コードは?しかし、そのコードは公開されていません。つまり、コードはライブラリのインターフェースに配置されておらず、潜在的に安全ではありません。
アントニオ

@Antonio「公開」と「インターフェース」の誤った定義を使用していると思います。Cライブラリのインターフェイスは、ヘッダーファイル内の外部変数、型、マクロ、および関数宣言です。fmin / fmaxはヘッダーファイルで宣言されているため、公開されているといいます。あなたが安全でないと言っているものはわかりません。
rationalcoder

21

これはかなり最近の開発による遅い答えです。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 ++コードでは使用できません(使用しないでください)。ただし、autoC ++ 11 を使用する場合の優れた型推論機能を使用しない理由はありません。

とはいえ、マクロがスコープに含まれている場合は、この構文を使用しても問題は発生しない思いextern "C" { ... }ます。たとえば、Cヘッダーから。私の知る限り、この拡張機能はその方法を見つけることができませんでしたinfo clang


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

マクロの問題を認識した功績はあるものの、関数の方がおそらく優れていると私は考えています:-)
paxdiablo

@paxdiablo-質問にはc-preprocessorタグがありますが、同意します。gccの__always_inline__属性のようなものを使用しない限り、関数は上記のキーワードを使用してもインライン化されるとは限りません。
ブレットヘイル

11

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

私は賛成しましたが、アンダースコアで始まり、その後に大文字が続く識別子は予約されています。
dreamlax 2016年

8

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

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


1
コンパイラーが十分に賢い場合、分岐を回避できます
Axel Gneiting

2
最適化がオンになっている場合、ほとんどの場合、すべての最新のコンパイラーは分岐ではなく条件付き移動を発行するため、このようなハックを使用しても意味がありません。
KrzysztofKosiński、2015年

1
確かに、当時何を見ていたのかわかりません。久しぶりです。x86とarmv7aの両方で、gccとclangはどちらも-Oを使用して分岐を回避します。
cib 2015年

6

@David Titarencoがここ釘付けにしたが、見栄えをよくするために少なくとも少し片付けて、両方min() max()一緒にここからのコピーと貼り付けを簡単にできるようにした。:)

2020年4月25日更新:セクション3も追加しました。これは、CとC ++の両方を学んだり、一方から他方に移行したりするための貴重な比較として、C ++テンプレートでもどのように行われるかを示しています。私はこの答えを繰り返し参照できる正規の参照にするために、徹底的かつ事実的かつ正確であるように最善を尽くしました。あなたがそれが私と同じくらい役立つことを願っています。

1.古いCマクロの方法:

この技術は、一般的に、適切に使用されている場合、それを適切に使用する方法を知っている人、「事実上の」物事の方法、および使用に罰金尊敬、使用しているが、バギー(と思う:ダブル評価副作用を)あなたの場合変数代入を含む式を渡して比較します:

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

2.新しく改善されたgcc " ステートメント式 "の方法:

この手法は、上記の「二重評価」の副作用とバグを回避するため、優れた、より安全な、「より現代的な」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になるため、実質的に値はありません。)

3. C ++テンプレートの方法:

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 ++テンプレートの発明の後に認められたことから、) intfloatなど、)の代わりのみのクラスタイプ。

ここでは、入力タイプと戻り値タイプの両方がであることがわかりますconst T&。これは、「タイプへの定数参照」を意味しTます。つまり、入力パラメーターと戻り値は、値渡しなく参照渡しになります。これはポインタによる受け渡しに似ており、クラスオブジェクトなどの大きな型の場合により効率的です。constexpr関数の一部は関数自体変更し、関数がコンパイル時に評価できる必要があることを示します(少なくともconstexpr入力パラメーターが提供されている場合)。ただし、コンパイル時に評価できない場合、デフォルトでaに戻ります。他の通常の関数と同様に、ランタイム評価。

constexprC ++関数のコンパイル時の側面は、Cマクロのようなものです。つまり、constexpr関数のコンパイル時の評価が可能な場合、それは、MIN()またはMAX()マクロの置換が可能であるのと同じように、コンパイル時に行われます。CまたはC ++でもコンパイル時に完全に評価されます。このC ++テンプレート情報のその他のリファレンスについては、以下を参照してください。

参照:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. CのMINおよびMAX
  4. 2020年4月に追加された追加のC ++テンプレート参照:
    1. ***** ウィキペディア:テンプレート(C ++) <-C ++テンプレートに関する素晴らしい情報!
    2. (私自身の質問と回答):なぜ `std :: max()`のC ++ 14テンプレートプロトタイプの `constexpr`部分なのですか?
    3. `constexpr`と` const`の違い

ウィキペディアの Clangのメモ:

[Clang]は、GNU Compiler Collection(GCC)のドロップイン代替として機能するように設計されており、そのコンパイルフラグと非公式言語拡張のほとんどをサポートしています。


過去24時間の反対投票者へ:朗報です!昨日セクション3で追加したセクション4の暴言を削除し、代わりここに配置しました。私は多くの良い情報をそこに入れ、すべての人に利益をもたらす確実で役立つ標準的な答えにするために最善を尽くしたので、私の答えを再評価し、必要に応じて賛成票を与えることを歓迎します。今は集中に戻っています。:)ありがとう!
ガブリエルステープルズ

4

それは私はあなたが定義した場合だと思う指摘価値がある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)

それでもNaNでは機能しません。fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
greggo 2018

@greggo、私はここでより良い答えを出しましたstackoverflow.com/a/30915238/2542702
Zボソン

4

Windef.h(a la #include <windows.h>)にはマクロmaxmin(小文字)マクロがあるように見えますが、これらも「二重評価」の困難さに悩まされていますが、独自のマクロを再ロールしたくない人のためにあります:)


12
あなたも驚いていますか?
マットジョイナー

2

男が「C」と言ったのは知っていますが、機会があればC ++テンプレートを使用してください。

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

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


16
引数はconst参照である必要があります。ユーザーが渡す内容はわかりません。
nmikhailov 2013年

6
そのような関数はすでに標準化されています(std :: min)。
dreamlax 2017年

C ++には、ほとんどの通常の目的のために多くの標準関数があります。ホイールを再発明しないでください。ただし、MSは独自の最小/最大値も定義するため、問題が発生することがあります
phuclv

0

二つの整数の最大値aとがbあります(int)(0.5((a+b)+abs(a-b)))。これはまたでも動作(double)し、fabs(a-b)ダブルスのために(フロートについても同様)


これが間違っている場合は申し訳ありませんが、私はC初心者ですが、このコードは私にとっては機能します
NRZ

2
それが整数以外で動作するかどうかはわかりません。浮動小数点演算の精度は非線形です。
Treesrule14 14

@ Treesrule14のコメントを拡張すると、コンピューターは数値を数学者と同じ方法で処理しないため、これは機能しません。浮動小数点には丸めの問題があるため、正しい答えが得られるとは限りません。整数演算を使用する場合でも、MAX_INT + MAX_INTは-2を与えるため、式を使用したmax(MAX_INT、MAX_INT)は-1として出力されます。
user9876

-3

最も簡単な方法は.h、プログラムをファイル内でグローバル関数として定義し、プログラムが多数のファイルでモジュール化されている場合は、必要なときにいつでも呼び出すことです。そうでない場合double MIN(a,b){return (a<b?a:b)}は、最も簡単な方法です。


1
@technosaurusこのソリューションが間違っている理由だけでなく、なぜこのソリューションが間違っているのかを説明すると役立つでしょう。
Tur1ng

@technosaurus、あなたの応答は本当に役に立ちません。Tur1ing、関数が完全に間違って定義されているように見え(入力パラメーターの型が欠落し、returnステートメントの後にセミコロンが欠落)、int入力をdoubleに変換することは物事を行うには不十分な方法であるため、型はdoubleであってはなりません。ここでは、定義またはステートメント式の方が優れています(例:ここを参照)。ただし、関数の場合、int32_t型、uint32_t型、float型またはdouble型の合計に対して3つの関数を作成することを検討してください。さまざまな機能。
ガブリエルステープルズ

1
@GabrielStaplesこの回答は回答ではないとしてフラグを立てる必要があります-それを助けることはできません。それは、最小のスペースで最も間違っている方法の例として使用できますが。ヘッダーでグローバル関数を推奨すると(静的インラインでもない?)、コンパイルユニットが2つ以上あるとコードが壊れ、コンパイルもできず、マクロのような関数の名前が付けられ、1989のような暗黙のint、明示的な理由なしにdoubleが返される、暗黙のせいぜい警告を引き起こすキャスト...そして最も重要なことに、それは質問に答えません-ジェネリックではなく、タイプセーフではなく、間違いなく最善の方法ではありません
テクノサウルス

これらの問題のそれぞれは、十分に詳細にカバーすることができないさらなる批判に値します。
テクノサウルス'28 / 10/28
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.