のようだ
if (x=y) { .... }
それ以外の
if (x==y) { ... }
諸悪の根源です。
すべてのコンパイラが構成可能な警告ではなくエラーとしてマークしないのはなぜですか?
if (x=y)
この構造が役立つケースを見つけることに興味があります。
のようだ
if (x=y) { .... }
それ以外の
if (x==y) { ... }
諸悪の根源です。
すべてのコンパイラが構成可能な警告ではなくエラーとしてマークしないのはなぜですか?
if (x=y)
この構造が役立つケースを見つけることに興味があります。
1 つの便利な構文は、たとえば次のとおりです。
char *pBuffer;
if (pBuffer = malloc(100))
{
//continue to work here
}
編集:
前に述べたように、今は何度か反対票を投じています。私もこれを見new
たことがありますが、胸がさらに痛くなります。
論争の少ない別の例は次のとおりです。
while (pointer = getNextElement(context))
{
//go for it, use the pointer to the new segment of data
}
これは、ループが終了するように、次の要素がない場合に関数getNextElement()
が戻ることを意味します。NULL
ほとんどの場合、コンパイラは下位互換性を維持するために非常に努力しています。
この問題で動作を変更してエラーをスローすると、既存の正当なコードが破損し、警告をスローし始めても、コードを自動的にコンパイルしてエラーと警告をチェックすることでコードを追跡する自動システムで問題が発生します。
これは、私たちが ATM でほとんど立ち往生している悪ですが、回避してその危険性を軽減する方法があります。
例:
void *ptr = calloc(1, sizeof(array));
if (NULL = ptr) {
// some error
}
これにより、コンパイル エラーが発生します。
簡単な答え: x=y などの代入演算には、x に新しく割り当てられた値と同じ値があります。これは比較で直接使用できるため、代わりに
x = y; if (x) ...
あなたは書ける
if (x = y) ...
書く (そして読む) コードが少なくて済むのは良いことですが、最近ではほとんどの人が、読みやすさを向上させるために別の方法で書くべきであることに同意しています。たとえば、次のようにします。
if ((x = y) != 0) ...
これが現実的な例です。malloc でメモリを割り当てたいと仮定し、それが機能するかどうかを確認します。次のように段階的に書くことができます:
p = malloc(4711); if (p != NULL) printf("Ok!");
NULL との比較は冗長なので、次のように書き直すことができます。
p = malloc(4711); if (p) printf("Ok!");
ただし、代入操作には使用可能な値があるため、代入全体を if 条件に入れることができます。
if (p = malloc(4711)) printf("Ok!");
これは同じことを行いますが、より簡潔です。
それは違法ではなく(とにかくCまたはC ++で)、時には役立つからです...
if ( (x = read(blah)) > 0)
{
// now you know how many bits/bytes/whatever were read
// and can use that info. Esp. if you know, say 30 bytes
// are coming but only got 10
}
とにかく割り当てを括弧で囲まないと、ほとんどのコンパイラは本当に悪臭を放ちます。これは私が気に入っています。
問題は、問題を逆さまに捉えていることです。「if」表記は、他の言語のように 2 つの値を比較することではありません。
C/C++ の「if」命令は、ブール値、または null/null 以外の値に評価される式を待機します。この式には、2 つの値の比較を含めることも、さらに複雑にすることもできます。
たとえば、次のことができます。
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
これは、C/C++ では、if 式が == と = に限定されないことを証明しています。true または false (C++)、またはゼロ以外のゼロ (C/C++) として評価できる限り、何でも構いません。
別の C++ の有効な使用法:
if(MyObject * pObject = dynamic_cast<MyInterface *>(pInterface))
{
pObject->doSomething() ;
}
そして、これらは if 式の単純な使用法です (これは for ループ宣言行でも使用できることに注意してください)。より複雑な用途が存在します。
でこの質問の重複を発見した後どの場合に if(a=b) が良い考えですか? 、追加のボーナス、つまりスコープへの変数の挿入でこの回答を完了することにしました。これはif
、変数宣言を含むその式を評価するためです他の言語:
だから、私自身からの引用:
もう 1 つの用途は、C++ 変数インジェクションと呼ばれるものを使用することです。Java には、次のクールなキーワードがあります。
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
C++ でも、それを行うことができます。正確なコードを念頭に置いているわけではありません (また、それを発見した正確な DDJ の記事もありません) が、この単純な定義はデモ目的には十分なはずです。
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
これは同じ方法で、注入を if および for 宣言と組み合わせて、プリミティブな foreach マクロを宣言できます (産業用強度の foreach が必要な場合は、boost を使用します)。
単純ではなく、より完全で堅牢な実装については、次の記事を参照してください。
めったに。実際、私はまだ覚えていません。私は 8 年間プロとして働いています。たまたまだったと思いますが、その後、8年間でかなりの量のバグを作成しました. この種のバグは、私がイライラして覚えているほど十分に発生しなかっただけです.
C では、次のようなバッファ オーバーランが原因で、さらに多くのバグが発生します。
void doSomething(char * p)
{
strcpy(p, "Hello World, how are you \?\n") ;
}
void doSomethingElse()
{
char buffer[16] ;
doSomething(buffer) ;
}
実際、Microsoft は、Visual C++ 2008 で strcpy を廃止するという警告を追加したため、非常に大きな打撃を受けました!!!
このエラーに対する最初の「保護」は、式を「変える」ことです。定数に値を代入できないため、次のようになります。
if(0 = p) // ERROR : It should have been if(0 == p). WON'T COMPILE !
コンパイルされません。
しかし、これは非常に貧弱な解決策だと思います。なぜなら、一般的なプログラミングの実践であるべきことをスタイルの背後に隠そうとするからです。
たとえば、次の代わりに:
void doSomething(char * p)
{
if(p == NULL) // POSSIBLE TYPO ERROR
return ;
size_t length = strlen(p) ;
if(length == 0) // POSSIBLE TYPO ERROR
printf("\"%s\" length is %i\n", p, length) ;
else
printf("the string is empty\n") ;
}
できるだけ多くの変数を "const" しようとすると、"if" 式内にないものも含め、ほとんどのタイプミスを回避できます。
void doSomething(const char * const p) // CONST ADDED HERE
{
if(p == NULL) // NO TYPO POSSIBLE
return ;
const size_t length = strlen(p) ; // CONST ADDED HERE
if(length == 0) // NO TYPO POSSIBLE
printf("\"%s\" length is %i\n", p, length) ;
else
printf("the string is empty\n") ;
}
もちろん、常に可能であるとは限りません (一部の変数は変更する必要があるため) が、私が使用する変数のほとんどが定数であることがわかりました (一度初期化してから、それらを読み取るだけです)。
通常、if(0 == p) 表記を使用しているコードを目にしますが、const 表記はありません。
私にとっては、リサイクルできるもの用のゴミ箱と、リサイクルできないもの用のゴミ箱があり、最後にそれらを同じ容器に一緒に捨てるようなものです.
そのため、コードが大幅に改善されることを期待して、簡単なスタイルの習慣をオウムしないでください。それはしません。可能な限り言語構造を使用してください。つまり、この場合、可能な場合は if(0 == p) 表記を使用し、可能な限り const キーワードを使用します。
'if(0 = x)' イディオムは、両側が変数 ('if(x = y)') であり、ほとんどの場合 (すべて?) 定数変数を使用する必要がある場合には役に立たないため、ほとんど役に立ちません。マジックナンバーではなく。
私がこのイディオムを決して使用しない他の 2 つの理由は、私見ですが、コードが読みにくくなります。コードを入念にテストすると (私たち全員がそうしていることは明らかです)、この種の構文エラーはすぐに現れます。
反復のための標準的な C 慣用句:
list_elem* curr;
while ( (curr = next_item(list)) != null ) {
/* ... */
}
多くのコンパイラはこれを検出して警告しますが、警告レベルが十分に高く設定されている場合のみです。
例えば:
~> gcc -c -ウォール foo.c foo.c: 関数 'foo' 内: foo.c:5: 警告: 真の値として使用される代入を括弧で囲むことを提案します
これは本当によくあるエラーですか?私は自分で C を学んだときにそれについて学びました。教師として、ときどき生徒に警告し、それはよくある間違いだと伝えましたが、実際のコードでは、初心者であってもほとんど見たことがありません。確かに、たとえば「||」の代わりに「&&」を書くなど、他のオペレーターの間違いよりも頻繁ではありません。
したがって、コンパイラがそれをエラーとしてマークしない理由 (完全に有効なコードであることを除いて) は、おそらくそれが非常に多くの悪の根源ではないからです。
言語によって異なります。if 括弧内で使用できるのはブール式のみであるため、Java はエラーとしてフラグを立てます (2 つの変数がブール値でない限り、代入もブール値です)。
C では、malloc によって返されたポインターをテストするための非常に一般的なイディオムです。または、フォーク後に親プロセスまたは子プロセスにいる場合:
if ( x = (X*) malloc( sizeof(X) ) {
// malloc worked, pointer != 0
if ( pid = fork() ) {
// parent process as pid != 0
C/C++ コンパイラは、必要に応じて十分に高い警告レベルで警告しますが、言語で許可されているため、エラーと見なすことはできません。ただし、コンパイラに警告をエラーとして処理するように依頼しない限り。
定数と比較するときは常に、テスト定数==変数を使用して、ユーザーが 2 番目の等号を忘れたかどうかをコンパイラが検出できるようにすることを提案する作成者もいます。
if ( 0 == variable ) {
// the compiler will complaint if you mistakenly
// write =, as you cannot assign to a constant
とにかく、可能な限り高い警告設定でコンパイルするようにしてください。
C および C++ 言語の設計者は、それを禁止しても実際には何の役にも立たないことに気付いたと思います。
それを許可することに伴う複雑さはありません。C++ は、暗黙的に変換可能な式が必要であると言っているだけbool
です。Cでは、他の回答で詳述されている便利なケースがあります。C++ では、さらに一歩進んで、さらにこれを許可しました。
if(type * t = get_pointer()) {
// ....
}
これは、実際には t のスコープを if とその本体のみに制限します。
その一部は、個人のスタイルと習慣に関係しています。if (kConst == x) または if (x == kConst) のいずれかを読み取ることにとらわれません。左の定数は使用しません。なぜなら、歴史的に私はそのような誤りを犯しておらず、私が言うように、または読みたいと思うようにコードを書いているからです。これは、自己認識し、向上するエンジニアになるための個人的な責任の一環としての個人的な決定であると考えています。たとえば、私は自分が作成しているバグの種類を分析し始め、それらを作成しないように自分の習慣を再設計し始めました.
とは言っても、コンパイラの警告は、歴史的にかなりくだらないものであり、この問題は何年も前からよく知られていましたが、80 年代後半になるまで製品コンパイラでは見られませんでした。また、移植性のあるプロジェクトに取り組むことで、私の C を大幅にクリーンアップすることができました。さまざまなコンパイラ、さまざまなテイスト (つまり、警告)、さまざまな微妙なセマンティックの違いなどです。
条件ステートメントでの代入演算子の優れた使用法は数多くありますが、それぞれの代入演算子について常に警告が表示されるのは大変なことです。IDE の関数で、同等性チェックの代わりに代入が使用されているすべての場所を強調表示できるようになると便利です。または、次のように記述した後:
if (x = y) {
次に、その線が数回点滅します。正確には標準的ではないことを行ったことを知らせるには十分ですが、迷惑になるほどではありません。
個人的には、これが最も有用な例だと思います。
読み取ったバイト数を返す関数read()
があり、これをループで使用する必要があるとします。使い方はずっと簡単です
while((count = read(foo)) > 0) {
//Do stuff
}
ループヘッドから割り当てを取得しようとするよりも、次のような結果になります
while(1) {
count = read(foo);
if(!(count > 0))
break;
//...
}
また
count = read(foo);
while(count > 0) {
//...
count = read(foo);
}
最初の構造はぎこちなく感じ、2 番目の構造は不快な方法でコードを繰り返します。
もちろん、これについての素晴らしいイディオムを見逃していない限り...
見てみる
if( life_is_good() )
enjoy_yourself();
なので
if( tmp = life_is_good() )
enjoy_yourself();
条件付きとしての割り当ては正当な C および C++ であり、それを許可しないコンパイラは実際の C または C++ コンパイラではありません。(C++ のように) C と明示的に互換性があるように設計されていない最新の言語が、それをエラーと見なすことを願っています。
これにより、C で文字列をコピーする慣用句など、簡潔な表現が可能になる場合もありますがwhile (*dest++ = *src++);
、全体的にはあまり役に立たず、言語設計上のミスと見なされます。私の経験では、この間違いを犯しやすく、コンパイラが警告を出さないときを見つけるのは困難です。
私はこのタイプミスを15年間の開発で一度だけ経験しました。私はそれが私の注意すべき事柄のリストの一番上にあるとは言いません。とにかくその構成も避けます。
一部のコンパイラ(私が使用しているコンパイラ)は、そのコードに対して警告を発行することにも注意してください。警告は、その価値のあるコンパイラのエラーとして扱うことができます。それらは無視することもできます。
比較の左側に定数を配置することは、防御的なプログラミングです。確かにあなたはその余分な「=」を忘れるというばかげた間違いをすることは決してないだろうが、誰が他の人について知っている。
有効なC/C ++であるため、コンパイラはエラーとしてフラグを立てません。ただし、(少なくともVisual C ++で)できることは、警告レベルを上げて警告としてフラグを立て、コンパイラーに警告をエラーとして処理するように指示することです。開発者が警告を無視しないように、これはとにかく良い習慣です。
==ではなく=を実際に意味していた場合は、それについてより明確にする必要があります。例えば
if ((x = y) != 0)
理論的には、これを実行できるはずです。
if ((x = y))
警告を上書きしますが、それが常に機能するとは限りません。
D プログラミング言語は、これをエラーとしてフラグ付けします。for
後で値を使用したいという問題を回避するために、C++ がループで許可するような宣言を許可します。
if(int i = some_fn())
{
another_fn(i);
}
これは、コピーなど、C/C++ の「低レベル」ループ構造で非常に一般的です。
void my_strcpy(char *dst, const char *src)
{
while((*dst++ = *src++) != '\0') { // Note the use of extra parentheses, and the explicit compare.
/* DO NOTHING */
}
}
もちろん、割り当ては for ループで非常に一般的です。
int i;
for(i = 0; i < 42; ++i) {
printf("%d\n", i);
}
ステートメントの外にあると、割り当てが読みやすくなると思います。if
char *newstring = malloc(strlen(src) * sizeof(char));
if(newstring == NULL) {
fprintf(stderr, "Out of memory, d00d! Bailing!\n");
exit(2);
}
// Versus:
if((newstring = malloc(strlen(src) * sizeof(char))) == NULL) // ew...
ただし、割り当てが明白であることを確認してください (最初の 2 つの例と同様)。それを隠さないでください。
偶発的な使用については... 私にはあまり起こりません。一般的な安全策は、変数 (左辺値) を比較の右側に置くことですが、次のようなものではうまく機能しません。
if(*src == *dst)
両方のオペランド to==
が左辺値であるためです!
コンパイラに関しては...誰が彼らを責めることができますか? コンパイラを書くのは難しいので、とにかくコンパイラのために完璧なプログラムを書くべきです (GIGO を覚えていますか?)。一部のコンパイラ (確かに最もよく知られているもの) は組み込みのlint
スタイル チェックを提供しますが、これは必須ではありません。一部のブラウザーは、スローされた HTML および Javascript のすべてのバイトを検証しないため、コンパイラーはなぜ検証するのでしょうか?
なぜ便利なのかと尋ねましたが、人々が提供している例に疑問を持ち続けてください. 簡潔なので助かります。
はい、それを使用するすべての例は、より長いコードとして書き直すことができます。
これを見つけるのに役立ついくつかの戦術があります.. 1つは醜く、もう1つは通常マクロです。話し言葉の読み方 (左から右、右から左) に大きく依存します。
例えば:
if ((fp = fopen("foo.txt", "r") == NULL))
対:
if (NULL == (fp = fopen(...)))
場合によっては、テストの内容を (最初に) 読み書きする方が簡単な場合があります。これにより、割り当てとテストを見つけやすくなります。次に、このスタイルを熱烈に嫌うほとんどの comp.lang.c 関係者を呼び込みます。
したがって、assert () を導入します。
#include <assert.h>
...
fp = fopen("foo.txt", "r");
assert(fp != NULL);
...
複雑な条件のセットの途中または最後にある場合、 assert() はあなたの友達です。この場合、FP == NULL の場合、abort() が発生し、問題のあるコードの行/ファイルが伝達されます。
それで、おっとした場合:
if (i = foo)
の代わりに
if (i == foo)
に続く
assert (i > foo + 1)
...そのような間違いをすぐに見つけることができます。
お役に立てれば :)
つまり、引数を逆にすると、デバッグ時に役立つ場合があります。 assert() は長年の友人であり、製品リリースのコンパイラ フラグでオフにすることができます。
他の回答で指摘されているように、条件内で代入を使用すると、必要なことを実行する簡潔だが読みやすいコードが提供される場合があります。また、最新のコンパイラの多くは、条件が必要な場所に代入がある場合に警告を発します。(開発に対するゼロ警告アプローチのファンなら、これらを見たことがあるでしょう。)
私がこれに悩まされるのを防ぐために開発した習慣の 1 つ (少なくとも C っぽい言語では) は、比較している 2 つの値のいずれかが定数 (または有効な左辺値ではない) である場合、それを入力することです。コンパレーターの左側:if (5 == x) { whatever(); }
次に、誤って を入力if (5 = x)
すると、コードはコンパイルされません。
実際には私はそれをしませんが、良いヒントは次のことです:
if ( true == $x )
equals を省略した場合、に代入$x
するtrue
と明らかにエラーが返されます。
正規表現のサンプル
正規表現 r; if(((r = new RegEx("\w*)).IsMatch()) { // ... ここで何かをする } else if((r = new RegEx("\d*")).IsMatch()) { // ... ここで何かをする }
価値テストを割り当てる
int i = 0; if((i = 1) == 1) { // 1 は、int 値 1 に割り当てられた i に等しい } そうしないと { // ? }
そのため、次のように書く方が良いです:
0 == 現在のアイテム
それ以外の:
現在のアイテム == 0
== の代わりに = を入力すると、コンパイラが警告するようにします。