Cで2つの文字列を連結する関数を記述したいとします。それを記述する方法は次のとおりです。
void concat(char s[], char t[]){
int i = 0;
int j = 0;
while (s[i] != '\0'){
i++;
}
while (t[j] != '\0'){
s[i] = t[j];
i++;
j++;
}
s[i] = '\0';
}
ただし、彼らの本の K&R は、特にwhileループの条件部分にできるだけ多くを含めて、異なる方法で実装しました。
void concat(char s[], char t[]){
int i, j;
i = j = 0;
while (s[i] != '\0') i++;
while ((s[i++]=t[j++]) != '\0');
}
どちらの方法が好ましいですか? K&Rのようにコードを記述することは推奨または推奨されませんか?私のバージョンは他の人にとって読みやすいと思います。
賢さよりも常に明快さを優先してください。過去数年間、最良のプログラマーは誰も理解できないコードを持つプログラマーでした。 "私は彼のコードを理解できません。彼は天才でなければなりません"、と彼らは言った。今日、最高のプログラマーは誰でもコードを理解できる人です。コンピュータの時間は、プログラマの時間よりも安いです。
どんな愚か者も、コンピューターが理解できるコードを書くことができます。優れたプログラマーは、人間が理解できるコードを作成します。 (M.ファウラー)
ですから、間違いなく、オプションAを選びます。それが私の決定的な答えです。
黄金のルールは、TulainsCórdovaの答えと同じように、わかりやすいコードを必ず書くことです。しかし、私はその結論に同意しません。その黄金律とは、コードを保守することになる最終的なプログラマーが理解できるコードを書くことを意味します。そしてあなたは、あなたのコードを保守することになる最終的なプログラマーが誰であるかについての最高の裁判官です。
Cで始まらなかったプログラマーにとって、最初のバージョンは、すでに知っている理由から、おそらく理解しやすいでしょう。
そのスタイルのCで育った人にとっては、2番目のバージョンの方が理解しやすいかもしれません。彼らにとって、コードが何をするかについても同様に理解できるので、コードの記述方法についての質問が少なくなり、彼らにとっても、 、垂直方向のスペースが少ないということは、より多くのコンテキストを画面に表示できることを意味します。
あなたは自分の良識に頼らなければならないでしょう。コードを最も理解しやすいようにしたい視聴者は誰ですか?このコードは会社向けに書かれていますか?その場合、ターゲットユーザーはおそらくその会社の他のプログラマーでしょう。これは、あなただけが取り組む個人的な趣味のプロジェクトですか?次に、あなたはあなた自身のターゲットオーディエンスです。このコードを他の人と共有しますか?次に、それらの他の人があなたのターゲットオーディエンスです。そのオーディエンスに一致するバージョンを選択します。残念ながら、推奨する単一の推奨方法はありません。
編集:行s[i] = '\0';
が最初のバージョンに追加され、以下のバリアント1で説明されているように修正されたため、これは現在のバージョンの質問のコードには適用されなくなりました。
2番目のバージョンには正しいであるという明確な利点がありますが、最初のバージョンはそうではありません-ターゲット文字列をnullで終了しません。
「条件付き代入」により、「すべての文字をコピーbeforenull文字をチェックする」という概念を非常に簡潔かつ最適化して、コンパイラは多少簡単ですが、最近の多くのソフトウェアエンジニアは、このスタイルのコードを読みにくくしています。最初のバージョンの使用を主張する場合は、次のいずれかを行う必要があります。
TulainsCórdovaとhvdの回答は、明快さ/読みやすさの側面を非常によくカバーしています。条件での割り当てを支持するもう1つの理由として、scopingを投入します。条件で宣言された変数は、そのステートメントのスコープでのみ使用できます。後でその変数を誤って使用することはできません。 forループは古くからこれを行ってきました。そして、次のC++ 17 ifとswitch に同様の構文を導入することは非常に重要です:
if (int foo = bar(); foo > 42) {
do_stuff();
}
foo = 23; // compiler error: foo is not in scope
いいえ。これは非常に標準的で通常のCスタイルです。あなたの例は悪いものです、それはforループであるべきだからですが、一般的には何も問題はありません
if ((a = f()) != NULL)
...
たとえば(またはwhileで)。
どちらのスタイルも適切に形成され、正しく、適切です。どちらがより適切かは、会社のスタイルガイドラインに大きく依存します。最新のIDEでは、他の方法では混乱の原因となる可能性のある領域を明示的に強調するライブ構文リンティングを使用して、両方のスタイルの使用を容易にします。
たとえば、次の式はNetbeansで強調表示されています。
if($a = someFunction())
「偶然の割り当て」を理由に。
「ええ、私は本当にそうするつもりでした...」とNetbeansに明示的に伝えるには、式を括弧のセットで囲むことができます。
if(($a = someFunction()))
結局のところ、それはすべて、企業スタイルのガイドラインと、開発プロセスを容易にする最新のツールの可用性にまで及びます。
K&R時代
while ((s[i++]=t[j++]) != '\0')
はほとんどのCPUで1つの命令にマップされます(12月のVACを期待しています)数日あります
(常に中かっこを使用する場合の注意–最初のコードセットは、「不要な」{}
、私の経験では、これらは多くの場合、コンパイラから不適切にマージされたコードを防ぎ、誤った「;」のエラーを許可しますツールによって検出される配置)
ただし、昔はコードの2番目のバージョンが読み取られていました。 (うまくいけば!)
concat(char* s, char *t){
while (*s++);
--s;
while (*s++=*t++);
}
これを行うことができるとしても、非常に悪い考えです。これは口語的に「世界の最後のバグ」として知られています。
if (alert = CODE_RED)
{
launch_nukes();
}
かなりほどの過ちを犯す可能性は低いですが、誤って失敗してコードベースで見つけにくいエラーを引き起こすことは非常に簡単です。最新のコンパイラのほとんどは、条件付きの代入に対して警告を挿入します。それらは理由のためにそこにあります、そしてあなたはそれらに耳を傾け、この構造を避けるためにうまくやるでしょう。