現代のCの書き方について、一般に受け入れられているガイドラインはありますか?


13

私はJava / Groovyの強力なバックグラウンドを持ち、管理ソフトウェア用の非常に大きなCコードベースを維持するチームに割り当てられました。

データベース内のblobの処理やPDFおよびExcelでのレポートの生成など、いくつかの問題点はJava Webサービスに外部化されています。

ただし、Java開発者として、コードのいくつかの側面に少し混乱しています。

  • それは冗長です(特に「例外」を扱う場合)
  • 巨大なメソッドがたくさんあります(2000行以上のメソッド)
  • 高度なデータ構造はありません(リスト、セット、およびマップをたくさん見逃しています)
  • 懸念事項の分離なし(SQLはコード全体でうれしそうに混合されています)

その結果、ビジネスは大量の技術コードに隠されており、Object Orientedと関数型プログラミングのピンチで形作られた私の脳は楽ではないと感じています。

プロジェクトの良い面は、コードが単純であることです。フレームワーク、実行時のバイトコード操作、AOPはありません。また、サーバーは、Javaが「hello world」を吐き出すのに必要なメモリよりも少ないメモリを使用することにより、1台のマシンで10000人以上のユーザーに同時に応答できます。

私は、一般に受け入れられている現代の原則に従ってCコードを書く方法を学びたいです。現代のCをどのように記述し構造化するかについて、一般に受け入れられている原則はありますか?

「Effective Java」本に相当するものに少し似ていますが、C用です。

回答とコメントに照らして編集:

  • 私は自分の考え方をCコードに適合させ、OOPにミラーリングしようとはしません。
  • コメントから推奨されるコーディングスタイルガイド(The GNU Coding StandardsおよびThe Linux Kernel Coding Style)をスキャンして読み始めました。
  • 次に、このコードスタイルを同僚に提案しようとします。最も難しいのは、同僚に、巨大なメソッドを小さなパーツに分割し、同じ4行のエラー処理コードを繰り返すことをメソッドの助けによって回避できることを納得させることです。

5
アプリケーションは実際にモダナイズする必要がありますか、それとも記述された方法がなじみがないために必要だと思いますか?
Blrfl


1
@Blrfl、私はアプリケーションが時代遅れの標準で書かれていると感じています。(管理)Cの現在の標準(2016年)を知りたいだけです。現在のアプリをリファクタリングしたり、形を変えたりしたくありません。コードの次の部分をどのように書くべきかを考えたいです。
ギヨーム


3
@antlersoft:単純なものの長いリストを次々に実行する2,000行の関数は、まったく問題なく、言い訳を必要としません。「2,000行の関数を書くべきではないので、2,000行の関数を書くべきではない」などの循環引数で返信しないでください。
gnasher729

回答:


14

あなたの質問から、問題はコードが古いCであるということではなく、単に悪いプログラミングであることがわかります。あなたが言及した問題のほとんどは、冗長性、巨大な2000行以上の関数、または懸念の分離がないことは、C、Javaのいずれの言語にも当てはまります。

冗長性は、エラー処理のコンテキストで言及されました。あなたは例を提供しなかったので、エラー処理コードもコードであることを思い出すことができます。定型コードの反復セクションの言い訳はありません。因数分解します。関数に(または別の関数を作成する価値がない場合)goto Error;パターンを実行し、エラー処理とリソースのクリーンアップをError:関数の下部のセクションに移動します。

エラーを呼び出しチェーンに渡すことが問題であると思われる場合は、自問してください:そこにある関数は、ここにいる小さな男に問題があることを本当に知る必要がありますか?言語に組み込まれている例外メカニズムを使用すると簡単に実行できますが、一般的には、エラー条件が高レベルコードのロジックを汚染しないように、例外を早期に(任意の言語で)処理することをお勧めします。そこまでの機能があれば、本当に知っている必要があり、そこに方法がありますエミュレート例外ではsetjmplongjmp

言及されている実際にCに関連する唯一の問題は、標準のコンテナがないことだと思います。一方でSet一般的で内缶がソートされた配列に置き換えMapペアの配列やで(大部分)struct(あなたが手の前にキーセットを知っていれば、map[key] = valueにターンs.key = value)が、実際には、標準的には、動的配列のコンテナがありませんです図書館。C99では、少なくともスタック(int array[len])で可変長配列を宣言できますが、len事前に計算する必要があり(通常は難しくありません)、もちろん、スタックに割り当てられたオブジェクトとして返すことはできません。ほとんどのプロジェクトは、独自の動的配列コンテナーを作成するか、オープンソースのコンテナーを採用することになります。

最後に、私はそこにいたことを指摘したいと思います。私はC ++と純粋なCに移行したJavaプログラマーでした。「良いCを学ぶために本Xを読む」ことをお勧めしますが、Javaにはないようなものはありません。前進する方法は、言語と標準ライブラリのすべての複雑さを吸収することです。Cで考え始めるまで、たくさんのgoogleを読み、たくさんのコードを書いてください。JavaのようにCで書くことは、母語から直接翻訳された言葉で外国語の文を書くのと同じくらいイライラします舌; あなたと読者の両方がうんざりするでしょう。良いニュースは、優れたプログラミングの学習は遅いが、別の言語の学習は速いということです。したがって、Javaで適切なコードを記述する場合、


1
全体として、これは本当に良い答えです。私はsetjmp()/ longjmp()を有効なツールとして見ることに反対します:クリーンアップを実行しようとさえしません。割り当てがリークされ、保持されたロックが解放されず、開かれたファイルが閉じられず、一時的なデータの不整合が永続的になります。私見、この関数ペアは基本的にこれまでに発明された最悪のハックであり、それを実装することが可能であったという唯一の正当性を持っています。最後に、Cでエラー処理を行う有効な方法は、明示的なエラーコードのみです。
cmaster-モニカの

@cmaster yea。個人的にsetjmp/longjmpは、Cでは水から魚が出ているように見えますが、私はそれらを使用したことはありません。例外を模倣するためのインターネット上の多数のチュートリアル/ライブラリのために、それらを含めることを強いられたので、実際にそれを使用する人がいると思いました。
フクロウ

7

プロジェクトの良い面は、コードが単純であることです。フレームワーク、実行時のバイトコード操作、AOPはありません。また、サーバーは、Javaが「hello world」を吐き出すのに必要なメモリよりも少ないメモリを使用することにより、1台のマシンで10000人以上のユーザーに同時に応答できます。

これがあなたの時間の価値があるかどうか、そしてコードの複雑さの低い作業ソフトウェアの「近代化」にリソースを費やす会社のお金に注意することをお勧めします。特に不慣れなシステムであるため、新しいバグを自分で導入する可能性が高いです。

それでもそのルートを下る場合は、次のことをお勧めします。

  • ソフトウェア/コードの状態図を作成(または生成)
  • コードに飛び込み、それぞれコードの最も複雑な部分または重要な部分のリストを作成します
  • そのコードベースに精通している人を見つけて、なぜそれがこのように構築されたのか、そして問題を引き起こすことが知られているものを尋ねる
  • 学んだことからドキュメントを書く

この時点で、これを調査する価値があるかどうかを決定します。あなたの会社の文化が失敗に報いられないなら、上層部またはマネージャーから青信号を得てください。

  • ソフトウェアのさまざまな構成要素を区画化し、それぞれの単体テストを記述します。
  • 異なるモジュールを接着できるまで繰り返します
  • 実際のユーザーインタラクションをシミュレートするテストをさらに行います(ストレステストなど)。

それはかなり良いロードマップであり、必要な場所に連れて行ってくれると思います。このプロジェクトの詳細を知らなければ、あなたを大いに助けることは困難です。私の免責事項を過度に警戒心があるとして捨てないでください。多数の優秀なプログラマーが、既存のプロジェクトをお気に入りの言語に書き換えようとしたり、「最新の」ツールを使用したりすることで、塵を打ち負かしています。これは慎重に検討する必要のある決定であり、経営陣のサポートや同僚の支援なしに、不正に進んで自分でそれをしないことをお勧めします。


2
私の質問がまったく明確ではなかったことを実感します。コードをリファクタリングしたくありません。まったく。既存のコードベースをそのまま維持したい。しかし、私は新しい機能のために現代のCを書く方法を学びたいです。そして、ここで私は失われました。私が見つけたドキュメントのほとんどは、「モダン」Cの記述方法ではなく、Cでのコーディング方法に関するものです。「モダン」Cのようなものはないかもしれません
Guillaume

1

より高いレベルの言語を好む場合は、CコードやObjective-Cなど、Cコードと簡単に混在できる言語がいくつかあります。

あるいは、CとC ++にはかなりの互換性があります。コードベース全体をわずかな変更でC ++としてコンパイルできる場合があります。名前を変更する必要のある「クラス」または「テンプレート」という名前の変数がありますが、実際にはすべてです。(sizeof( 'a')はCとC ++では異なりますが、私はそれを使用したことがないと思います)。

その方法を採用する場合、次のメンテナーがC ++を流に扱えない可能性があることを考慮してください。夢中にならないでください。C ++を利用しますが、Cプログラマーが簡単に理解できる程度に限ります。


1
ここで意見が合わない。CおよびC ++は異なった言語であり、C ++コンパイラ(明示の戻り値をキャストすることによって、必要ないくつかのコードがmalloc)の意味C.で悪い習慣に考えられているconstinline非常に CとC ++の間で異なる、そしてもちろんCの++を理解していません。__restrict。両方でコンパイルされるソースのサブセットであっても、言語を交換可能として扱わないでください。
Angewは、

1

基本的に、優れたCコードを記述することは、優れたC ++またはJavaコードを記述することと同じstructです。クラスが必要な場合は、を使用します。継承が必要な場合は、struct名前のない最初のメンバーとしてベースを含めます。仮想関数が必要な場合は、静的struct関数ポインターにポインターを追加します。などなど。C++が内部で行うこととまったく同じです。唯一の違いは、Cで明示的であることです。したがって、Cで完全にオブジェクト指向プログラミングを行うことができます。に慣れた。

ポイントは、優れたプログラミングは言語機能ではなくパラダイムに関するものだということです。確かに、言​​語機能が使用したいパラダイムを適切にサポートしていれば、それは常に素晴らしいことですが、言語機能は必須ではありません。これに気づいたら、ほぼすべての言語(brainfuckやINTERCALなどの難解な言語を除く)で優れたコードを作成できます。

もちろん、標準Cライブラリには、慣れている気の利いたコンテナクラスが含まれていないという問題が残っています。残念ながら、これは独自のものを使用するか、動的に割り当てられた配列を使用してこの不足を回避する必要があることを意味します。しかし、すぐにわかると思います。本当に必要なのは、動的配列(malloc())と、クラス内のポインターメンバーを介して実装されるリンクリスト/ツリーだけです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.