28

私はすべてのコーディングを 1 つの C ファイルで行うことに慣れています。しかし、私は大規模なプロジェクトに取り組んでいるため、そうすることが現実的ではありません。私はそれらを一緒に #include してきましたが、一部のファイルを複数回 #include するなどのケースに遭遇しました。.h ファイルについて聞いたことがありますが、それらの機能が何であるか (または理由2 つのファイルを持つ方が 1 つよりも優れています)。

コードを整理するためにどのような戦略を使用する必要がありますか? 特定のファイルについて、「パブリック」関数を「プライベート」関数から分離することは可能ですか?

この質問は私の問い合わせを引き起こしました。tea.h ファイルは tea.c ファイルを参照しません。コンパイラは、すべての .h ファイルに対応する .c ファイルがあることを「認識」していますか?

4

8 に答える 8

36

.h ファイルは、.c ファイルのインターフェイス ファイルと見なす必要があります。すべての .c ファイルは、一定量の機能を持つモジュールを表します。.c ファイル内の関数が他のモジュール (つまり、他の .c ファイル) によって使用される場合は、関数プロトタイプを .h インターフェイス ファイルに入れます。元のモジュールの .c ファイルと関数が必要な他のすべての .c ファイルにインターフェイス ファイルを含めることで、この関数を他のモジュールで使用できるようになります。

特定の .c ファイル (他のモジュールではなく) の関数のみが必要な場合は、そのスコープを静的に宣言します。これは、定義されている c ファイル内からのみ呼び出すことができることを意味します。

複数のモジュールで使用される変数についても同様です。それらはヘッダー ファイルに入れ、キーワード「extern」でマークする必要があります。注: 関数の場合、キーワード「extern」はオプションです。関数は常に「extern」と見なされます。

ヘッダー ファイルのインクルード ガードは、同じヘッダー ファイルを複数回インクルードしないようにするのに役立ちます。

例えば:

Module1.c:

    #include "Module1.h"

    static void MyLocalFunction(void);
    static unsigned int MyLocalVariable;    
    unsigned int MyExternVariable;

    ボイド MyExternFunction(ボイド)
    {
        MyLocalVariable = 1u;       

        /* 何かをする */

        MyLocalFunction();
    }

    static void MyLocalFunction(void)
    {
      /* 何かをする */

      MyExternVariable = 2u;
    }

Module1.h:

    #ifndef __MODULE1.H
    #define __MODULE1.H

    extern unsigned int MyExternVariable;

    void MyExternFunction(void);      

    #endif

Module2.c

    #include "Module.1.h"

    static void MyLocalFunction(void);

    static void MyLocalFunction(void)
    {
      MyExternVariable = 1u;
      MyExternFunction();
    }
于 2008-09-06T23:29:14.977 に答える
10

各 .c が特定の機能領域に焦点を当てるようにしてください。対応する .h ファイルを使用して、これらの関数を宣言します。

各 .h ファイルには、コンテンツの周りに「ヘッダー」ガードが必要です。例えば:

#ifndef ACCOUNTS_H
#define ACCOUNTS_H
....
#endif

そうすれば、「accounts.h」を何度でも含めることができ、特定のコンパイル単位で最初に表示されたときだけが、実際にそのコンテンツを取り込むことができます。

于 2008-09-06T23:02:43.977 に答える
8

コンパイラ

このトピックで C 'モジュール' の例を見ることができます - ヘッダー tea.h とコード tea.c の 2 つのファイルがあることに注意してください。他のプログラムにアクセスさせたいすべての公開定義、変数、および関数プロトタイプをヘッダーで宣言します。メイン プロジェクトで #include すると、そのコードはヘッダーに記載されている tea モジュールの関数と変数にアクセスできるようになります。

その後、もう少し複雑になります。ビルドを管理する Visual Studio やその他の多くの IDE を使用している場合は、この部分を無視してください。オブジェクトのコンパイルとリンクを処理します。

リンカ

2 つの個別の C ファイルをコンパイルすると、コンパイラは個別のオブジェクト ファイルを生成します。したがって、main.c は main.o になり、tea.c は tea.o になります。リンカーの仕事は、すべてのオブジェクト ファイル (main.o と tea.o) を調べて参照を一致させることです。そのため、main で tea 関数を呼び出すと、リンカーはその呼び出しを変更して、実際に適切な関数を呼び出します。お茶の機能。リンカーは実行可能ファイルを生成します。

範囲や遭遇するその他の問題を含め、このテーマについてより深く掘り下げた優れたチュートリアルがあります。

幸運を!

-アダム

于 2008-09-06T23:21:15.230 に答える
7

開始するためのいくつかの簡単なルール:

  1. 「パブリック」にしたい宣言を、作成中の C 実装ファイルのヘッダー ファイルに入れます。
  2. C ファイルの実装に必要な #include ヘッダー ファイルのみを C ファイルに含めます。
  3. ヘッダー ファイル内の宣言に必要な場合にのみ、ヘッダー ファイルにヘッダー ファイルを含めます。

  4. アンドリューによって説明されたインクルード ガード メソッドを使用するか、コンパイラがサポートしている場合は#pragma once を使用します(これは同じことを行います - 時にはより効率的です)。
于 2008-09-06T23:12:13.410 に答える
3

上記の回答に加えて、コードをモジュール(個別のファイル)に分割することの小さな利点の1つは、グローバル変数が必要な場合に、キーワード'を使用してスコープを単一のモジュールに制限できることです。静的'。(これを関数に適用することもできます)。この「static」の使用は、関数内での使用とは異なることに注意してください。

于 2008-09-07T02:20:47.793 に答える
3

追加の質問に答えるには:

この 質問は私の問い合わせを引き起こしました。tea.h ファイルは tea.c ファイルを参照しません。コンパイラは、すべての .h ファイルに対応する .c ファイルがあることを「認識」していますか?

コンパイラは、主にヘッダー ファイルには関与しません。コンパイラを呼び出すたびに、ソース (.c) ファイルがオブジェクト (.o) ファイルにコンパイルされます。舞台裏 (つまり、makeファイルまたはプロジェクト ファイル) で、これと同等のコマンド ラインが生成されています。

compiler --options tea.c

ソース ファイル#includeは、参照するリソースのすべてのヘッダー ファイルです。これは、コンパイラがヘッダー ファイルを見つける方法です。

(ここでは詳細を省略しています。C プロジェクトのビルドについては、学ぶべきことがたくさんあります。)

于 2008-09-06T23:38:10.743 に答える
2

あなたの質問は、あなたがあまり本格的な開発を行っていないことを明らかにしています。通常、コードは大きすぎて 1 つのファイルに収まりません。適切なルールは、機能を論​​理ユニット (.c ファイル) に分割し、各ファイルには、一度に頭で簡単に保持できる範囲を超えないようにすることです。

通常、特定のソフトウェア製品には、さまざまな .c ファイルからの出力が含まれます。これが通常行われる方法は、コンパイラが多数のオブジェクト ファイルを生成することです (UNIX システムの ".o" ファイルでは、VC が .obj ファイルを生成します)。これらのオブジェクト ファイルを出力 (共有ライブラリまたは実行可能ファイル) に構成することが「リンカー」の目的です。

通常、実装 (.c) ファイルには実際の実行可能コードが含まれ、ヘッダー ファイル (.h) にはそれらの実装ファイルのパブリック関数の宣言が含まれます。実装ファイルよりも多くのヘッダー ファイルを簡単に作成できます。また、ヘッダー ファイルにインライン コードを含めることもできます。

一般に、実装ファイルが互いにインクルードされることは非常にまれです。各実装ファイルがその懸念事項を他のファイルから分離していることを確認することをお勧めします。

Linux カーネルのソースをダウンロードして確認することをお勧めします。これは C プログラムとしては非常に大規模ですが、機能の個別の領域に適切に編成されています。

于 2008-09-06T23:25:14.907 に答える
0

関数のプロトタイプを定義するには、.h ファイルを使用する必要があります。これは、必要なすべての関数を 1 つのファイルで宣言することなく、必要なプロトタイプを C ファイルに含めることができるようにするために必要です。

たとえば#include <stdio.h>、これは printf およびその他の IO 関数のプロトタイプを提供します。これらの関数のシンボルは、通常、デフォルトでコンパイラによってロードされます。これらのファイルに関連する通常のイディオムに興味がある場合は、/usr/include の下にあるシステムの .h ファイルを調べることができます。

多くの機能を持たない単純なアプリケーションを作成するだけであれば、すべてをモジュール化してプロシージャの論理グループにする必要はありません。ただし、大規模なシステムを開発する必要がある場合は、各関数をどこに定義するかについて考慮する必要があります。

于 2008-09-07T00:01:17.160 に答える