web-dev-qa-db-ja.com

純粋なC(C ++ではない)のコーディング標準

私はJavaバックグラウンド(CSクラスから)とC++の学期から来ました。純粋なCであるCo-OpのOpenCVプロジェクトを仕上げているところなので、この質問をするのに少し遅れました。

純粋なCの設計プロセスとコーディング標準は何ですか?

オブジェクト指向プログラミング、設計、およびベストプラクティスに精通しています。 Cのような非オブジェクト指向言語では少し途方に暮れています。すべての変数と関数はグローバルであるように見えます。それは私にとって本当の混乱のように感じさせます。

34
awakenDeepBlue

同様の質問 への回答をチェックすることに興味があるかもしれません。さらに、Cスタイルガイドに興味がある場合は、C(およびC++)スタイルガイドのリポジトリであるため、 このページ を参照することをお勧めします。 良い笑いを好むなら、 NASA Cスタイルガイド をご覧ください。特に、massiveコメントを見てください...私が話しているコメントがわかります。このようなコメントは書かないでください。

私は個人的に Indian Hill Cスタイルガイド をいくつか変更してお勧めします。さらに、Cで大規模なプログラムの設計に問題がある場合は、本 C Interfaces and Implementations を購入することをお勧めします。

31
anon

正直なところ、StackOverflowに関する答えがいくつもあれば、適切に構造化されたCプログラムを設計および作成する方法がわかるとは思いません。あなたは良い本を読む必要があります、そして読むのに明白なものは Cプログラミング言語 カーニハン&リッチーによるものです。

15
anon

私はCでの専門的な経験はありません(C++でのみ)。したがって、アドバイス、トリック、およびヒントは、「オブジェクトのような指向」であるため、あまり真剣に受け取らないでください。

ほとんどオブジェクトC?

基本的なオブジェクトのような機能のシミュレーションは簡単に実行できます。

ヘッダーで、型を宣言し、typedefで転送し、「メソッド」を宣言します。例えば:

/* MyString.h */

#include <string.h>

/* Forward declaration */
struct StructMyString ;

/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;

MyString *       MyString_new() ;
MyString *       MyString_create(const char * p_pString) ;
void             MyString_delete(MyString * p_pThis) ;
size_t           MyString_length(const MyString * p_pThis) ;

MyString *       MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString *       MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;

const char *     MyString_get_c_string(const MyString * p_pThis) ;
MyString *       MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString *       MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;

各関数に接頭辞が付いているのがわかります。 「構造体」の名前を選択して、別のコードと衝突しないようにします。

また、OOのようなアイデアを維持するために「p_pThis」を使用したことがわかります。

ソースファイルで、タイプを定義し、関数を定義します。

/* MyString.c */

#include "MyString.h"

#include <string.h>
#include <stdlib.h>

struct StructMyString
{
   char *      m_pString ;
   size_t      m_iSize ;
} ;

MyString * MyString_new()
{
   MyString * pMyString = malloc(sizeof(MyString)) ;

   pMyString->m_iSize = 0 ;
   pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
   pMyString->m_pString[0] = 0 ;

   return pMyString ;
}

/* etc. */

「プライベート」関数(またはプライベートグローバル変数)が必要な場合は、Cソースでそれらを静的に宣言します。このように、それらは外から見えなくなります:

static void doSomethingPrivate()
{
   /* etc. */
}

static int g_iMyPrivateCounter = 0 ;

継承が必要な場合は、ほとんど失敗しています。変数を含め、Cのすべてがグローバルであると考えている場合は、継承のシミュレーション方法を考える前に、Cの経験を積む必要があります。

その他チップ

複数のコードパスを避けてください。

たとえば、複数の返品は危険です。例えば:

void doSomething(int i)
{
   void * p = malloc(25) ;

   if(i > 0)
   {
      /* this will leak memory ! */
      return ;
   }

   free(p) ;
}

非constグローバルを避ける

これには、「静的」変数(静的関数ではない)が含まれます。

グローバル非const変数はほとんど常に悪い考えです(つまり、くだらない関数の例については、C API strtokを参照してください)。

名前の衝突を避ける

関数と定義のために「名前空間」を選択します。これは:

#define GROOVY_LIB_x_MY_CONST_INT 42

void GroovyLib_dosomething() ;

注意してください

定義はCでは避けられませんが、副作用がある可能性があります!

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

void doSomething()
{
   int i = 0, j = 1, k ;
   k = MAX(i, j) ;   /* now, k == 1, i == 0 and j == 1 */
   k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}

変数を初期化する

初期化せずに変数を宣言しないでください。

int i = 42 ; /* now i = 42 */
int j ;      /* now j can have any value */
double k ;   /* now f can have any value, including invalid ones ! */

初期化されていない変数は、苦痛なバグの原因です。

すべてのC APIを知っている

K&Rで説明されているC API関数リストは非常に小さいです。 20分でリスト全体を読むことができます。それらの機能を知っている必要があります。

何か体験したいですか?

C APIを書き換えます。たとえば、string.h関数の独自のバージョンを作成して、それがどのように行われるかを確認してください。

9
paercebal

関数またはオブジェクトのスコープを「静的」に宣言することにより、ソースファイルに対してローカルにすることができます。それは少し役立ちます。

そうでなければ、名前空間の衝突を回避するために私が見る典型的なイディオムは、名前にある種のファシリティ識別子を付けることです。たとえば、foo.cのすべての名前はfoo_ *になります。

8
T.E.D.

他の優れたCプログラマーと協力してください。彼らとコードレビューを行ってください。彼らにあなたのコードを見させるだけでなく、あなたは彼らのコードを見ます。

4
Robert

良いニュースは、Cでセミオブジェクト指向の方法でプログラミングできることです。データを保護したり、アクセス関数を公開したりできます。C++の空想のすべてを備えているわけではありませんが、他の人のC++コードについて私が見たものから、とにかく多くの人々は空想を使用しません。言い換えると、人々はクラス内でCコードを記述しますが、Cではクラスコンテナなしで同じコードを記述します。

まず、Cプログラミングとスタイルに関する本を読んでください。K&Rで結構です。次に、チェックアウト CERTプログラミング標準 をお勧めします。このサイトは主に「安全な」コーディング標準に焦点を当てていますが、ここにあるコンテンツの多くは、誰もが従うべき一般的なコード品質標準です。ここで説明したことを行うと、品質が向上し、厄介なバグがなくなり、副作用としてコードの安全性が高まります。

3
KFro

Linuxカーネルのソースをよく見たいと思うかもしれません。指向のプログラマーの場合、Cでのエラー処理は特に困難な作業になります。これが役立つかもしれません---> http://www.freetype.org/david/reliable-c.html

2
puffadder

純粋なCでオブジェクト指向の設計を行うことができます。簡単なアプローチは、モジュールをclassと見なし、パブリックメソッドをthisパラメータを明示的な最初の引数として必要とする通常の関数と見なすことです。

class名が関数名の接頭辞であり、すべてのプライベート関数とクラスデータがstaticと宣言されている場合に役立ちます。メモリを取得するためにmalloc()を使用してコンストラクタを作成し、データフィールドを明示的に初期化します。

完全にプライベートなデータメンバーを持つオブジェクトのコンストラクターは、不透明なポインター(void *偶数、またはタイプセーフが必要な場合は不完全な型へのポインタとして)。パブリックデータメンバーのみが必要な場合は、パブリックに定義されたstructへのポインターが適切に機能します。

このパターンの後にいくつかのライブラリが続きます。初期化関数は、すべてのライブラリメソッドに渡される必要があるCookieを返し、1つのメソッドがデストラクタとして機能します。

もちろん、純粋なCで適切に設計する方法は他にもありますが、OOでうまくいく場合は、完全に放棄する必要はありません。

1
RBerteig

ファイルスコープの変数と関数の可視性をそれぞれのソースファイルに制限できます(ただし、これらのオブジェクトへのポインターを渡すことを妨げるものではありません)。

例えば:

/** foo.c */
static void foo_helper() {...} /* foo_helper cannot be called by name 
                                  outside of foo.c */
static int local_state;        /* local state is visible at file scope,
                                  but is not exported to the linker */
1
John Bode

コーディングスタイル

systemd

systemdコード品質ツール

RIOT-OS

nginx

Gitソースのガイドライン

FFmpeg

カール

建築

オープンソースアプリケーションのアーキテクチャ Cプログラムには、Git、Nginx、Open MPI、GPSD(C&Python)、GDB、FreeRTOS、Berkeley DB、Bash、Asterisk、

VLC(たくさんのアーキテクチャ情報)

ソースの例

上記のプロジェクトはどれも読む価値があります。標準のコマンドラインツールも読むことができます。 Linuxカーネルにも多数の設計ドキュメントがあります。

0
novelistparty

重要な環境基準に関心がある場合は、MISRA Cが役立ちます。参考になりました。

MISRA C 2004 here を取得できます。

更新されたMISRA C 2012の概要 こちら

0
Michal M.