条件演算子の必要性は何ですか? if-else コンストラクトを実装しているため、機能的に冗長です。条件演算子が同等の if-else 代入よりも効率的である場合、なぜコンパイラは if-else をより効率的に解釈できないのでしょうか?
14 に答える
C での実際の有用性は、ステートメントではなく式であることです。つまり、ステートメントの右側 (RHS) に置くことができます。そのため、特定のことをより簡潔に書くことができます。
与えられた他の答えのいくつかは素晴らしいです。const
しかし、コンパクトな方法で正確さを強制するために使用できると誰も言及していないことに驚いています。
このようなもの:
const int n = (x != 0) ? 10 : 20;
したがって、基本的にn
は、const
初期値が条件ステートメントに依存する です。最も簡単な代替手段は、 make n
not aconst
です。これにより、普通のif
人がそれを初期化できるようになります。しかし、それを望むならconst
、普通の ではできませんif
。あなたが作ることができる最良の代用品は、次のようなヘルパー関数を使用することです:
int f(int x) {
if(x != 0) { return 10; } else { return 20; }
}
const int n = f(x);
しかし、三項 if バージョンははるかにコンパクトで、間違いなく読みやすいです。
三項演算子は、パフォーマンスのショートカットではなく、構文と可読性の利便性を目的としています。さまざまな複雑さの条件に対するメリットについては意見が分かれますが、短い条件については、1 行の式を使用すると便利な場合があります。
さらに、Charlie Martin が書いたように、これは式なので、C のステートメントの右側に表示できることを意味します。これは、簡潔であるために価値があります。
次のように、コードの難読化にとって重要です。
Look-> See?!
No
:(
Oh, well
);
コンパクトさと、if-then-else コンストラクトを式にインライン化する機能。
C には、技術的には不要なものがたくさんあります。それらは、他のことを考えると多かれ少なかれ簡単に実装できるからです。以下は不完全なリストです。
- その間
- 為に
- 機能
- 構造体
これらがなければコードがどのようになるか想像してみてください。答えが見つかるかもしれません。三項演算子は「シンタックス シュガー」の一種であり、注意とスキルを持って使用すると、コードの記述と理解が容易になります。
場合によっては、三項演算子が仕事を成し遂げるための最良の方法です。特に、三項の結果を左辺値にしたい場合。
これは良い例ではありませんが、より良いものに空白を描いています。確かなことの 1 つは、3 進数を実際に使用する必要がある場合ではありませんが、私はまだかなり使用しています。
const char* appTitle = amDebugging ? "DEBUG App 1.0" : "App v 1.0";
ただし、私が警告することの 1 つは、3 進数をつなぎ合わせることです。それらは
メンテナンス時に実際の問題になります。
int myVal = aIsTrue ? aVal : bIsTrue ? bVal : cIsTrue ? cVal : dVal;
編集:これは潜在的により良い例です。三項演算子を使用して、それを処理する関数を記述する必要がある場合に、参照と const 値を割り当てることができます。
int getMyValue()
{
if( myCondition )
return 42;
else
return 314;
}
const int myValue = getMyValue();
...になる可能性があります:
const int myValue = myCondition ? 42 : 314;
どちらが優れているかは、私が議論しないことを選択する議論の余地のある質問です.
三項演算子はステートメントではなく式であるため、式の一部として使用される関数のようなマクロのマクロ展開で使用できます。const は元の C の一部ではなかったかもしれませんが、マクロ プリプロセッサはずっと前のものです。
使用されている場所の 1 つは、境界チェック配列アクセスにマクロを使用する配列パッケージです。チェックされた参照の構文は のようなものaref(arrayname, type, index)
で、arrayname は実際には配列境界とデータの unsigned char 配列を含む構造体へのポインターであり、type はデータの実際の型で、index はインデックスでした。これの展開は非常に面倒でした (そして、私は記憶からそれを行うつもりはありません) が、境界チェックを行うためにいくつかの三項演算子を使用しました。
返されるオブジェクトのポリモーフィズムが必要なため、これを C の関数呼び出しとして行うことはできません。そのため、式で型キャストを行うにはマクロが必要でした。C++ では、テンプレート化されたオーバーロードされた関数呼び出し (おそらく operator[] 用) としてこれを行うことができますが、C にはそのような機能がありません。
編集: これは、Berkeley CAD アレイ パッケージ (glu 1.4 版) からの、私が話していた例です。array_fetch の使用法に関するドキュメントは次のとおりです。
type
array_fetch(type, array, position)
typeof type;
array_t *array;
int position;
配列から要素をフェッチします。配列の範囲外を参照しようとすると、実行時エラーが発生します。指定された位置の値が実際に配列を逆参照するときに使用される型であるかどうかの型チェックはありません。
array_fetch のマクロ定義は次のとおりです (単一の式の一部として正しい順序で正しい値を持つすべての部分式を実行するために、三項演算子とカンマ シーケンス演算子を使用することに注意してください)。
#define array_fetch(type, a, i) \
(array_global_index = (i), \
(array_global_index >= (a)->num) ? array_abort((a),1) : 0,\
*((type *) ((a)->space + array_global_index * (a)->obj_size)))
array_insert の展開 (必要に応じて C++ ベクトルのように配列を拡張します) は、複数のネストされた 3 項演算子を含む、さらに複雑です。
これについてはまだ誰も言及していないため、スマートprintf
ステートメントを取得する唯一の方法は、三項演算子を使用することです。
printf("%d item%s", count, count > 1 ? "s\n" : "\n");
警告: C から C++ に移行すると、演算子の優先順位にいくつかの違いがあり、それによって発生する微妙なバグに驚くかもしれません。
これはシンタティック シュガーであり、1 つのステートメントのみを含む簡単な if/else ブロックの便利な省略形です。機能的には、両方のコンストラクトが同じように機能する必要があります。
dwn が言ったように、パフォーマンスは、複雑なプロセッサが台頭したときの利点の 1 つでした。 if/else ステートメント。
次のコードを与えます。
#include <windows.h>
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
int array[10000];
int countthem(int boundary)
{
int count = 0;
for (int i = 0; i < 10000; i++) {
if (array[i] < boundary) count++;
}
return count;
}
int __cdecl wmain(int, wchar_t **)
{
for (int i = 0; i < 10000; i++) array[i] = rand() % 10;
for (int boundary = 0; boundary <= 10; boundary++) {
LARGE_INTEGER liStart, liEnd;
QueryPerformanceCounter(&liStart);
int count = 0;
for (int iterations = 0; iterations < 100; iterations++) {
count += countthem(boundary);
}
QueryPerformanceCounter(&liEnd);
printf("count=%7d, time = %I64d\n",
count, liEnd.QuadPart - liStart.QuadPart);
}
return 0;
}
異なる境界のコストは大きく異なり、奇妙です (元の資料を参照)。変更する場合:
if (array[i] < boundary) count++;
に
count += (array[i] < boundary) ? 1 : 0;
次の理由により、実行時間は境界値に依存しなくなりました。
オプティマイザは、三項式から分岐を削除できました。
しかし、私のデスクトップ intel i5 cpu/windows 10/vs2015 では、テスト結果は msdn ブログとはかなり異なります。
デバッグ モードを使用する場合、if/else コスト:
count= 0, time = 6434
count= 100000, time = 7652
count= 200800, time = 10124
count= 300200, time = 12820
count= 403100, time = 15566
count= 497400, time = 16911
count= 602900, time = 15999
count= 700700, time = 12997
count= 797500, time = 11465
count= 902500, time = 7619
count=1000000, time = 6429
三項演算子のコスト:
count= 0, time = 7045
count= 100000, time = 10194
count= 200800, time = 12080
count= 300200, time = 15007
count= 403100, time = 18519
count= 497400, time = 20957
count= 602900, time = 17851
count= 700700, time = 14593
count= 797500, time = 12390
count= 902500, time = 9283
count=1000000, time = 7020
リリース モードを使用する場合、if/else コスト:
count= 0, time = 7
count= 100000, time = 9
count= 200800, time = 9
count= 300200, time = 9
count= 403100, time = 9
count= 497400, time = 8
count= 602900, time = 7
count= 700700, time = 7
count= 797500, time = 10
count= 902500, time = 7
count=1000000, time = 7
三項演算子のコスト:
count= 0, time = 16
count= 100000, time = 17
count= 200800, time = 18
count= 300200, time = 16
count= 403100, time = 22
count= 497400, time = 16
count= 602900, time = 16
count= 700700, time = 15
count= 797500, time = 15
count= 902500, time = 16
count=1000000, time = 16
三項演算子は、私のマシンの if/else ステートメントよりも遅いです!
そのため、さまざまなコンパイラ最適化手法に応じて、外部演算子と if/else の動作が大きく異なる場合があります。
3 項 = if-else の単純な形式。主に読みやすさのために利用できます。
と同じ
if(0)
do();
if(0)
{
do();
}