web-dev-qa-db-ja.com

Cの静的関数

Cで関数を静的にすることのポイントは何ですか?

165
Cenoc

関数staticを作成すると、他の翻訳単位から非表示になり、 カプセル化 が提供されます。

helper_file.c

int f1(int);        /* prototype */
static int f2(int); /* prototype */

int f1(int foo) {
    return f2(foo); /* ok, f2 is in the same translation unit */
                    /* (basically same .c file) as f1         */
}

int f2(int foo) {
    return 42 + foo;
}

main.c

int f1(int); /* prototype */
int f2(int); /* prototype */

int main(void) {
    f1(10); /* ok, f1 is visible to the linker */
    f2(12); /* nope, f2 is not visible to the linker */
    return 0;
}
208
pmg

pmgはカプセル化に関するスポットです。関数を他の翻訳単位から隠す(またはbecause)以外に、関数staticを作成すると、コンパイラーの最適化が存在する場合でもパフォーマンス上の利点が得られます。

static関数は、現在の変換単位の外部から呼び出すことはできないため(コードがそのアドレスへのポインターを取る場合を除く)、コンパイラーはすべての呼び出しポイントを制御します。

つまり、非標準のABIを自由に使用したり、完全にインライン化したり、外部リンケージのある関数では不可能な他の最適化を実行したりできます。

76
Stephen Canon

Cのstaticキーワードはコンパイル済みファイル(.hではなく.c)で使用されるため、関数はそのファイルにのみ存在します。

通常、関数を作成すると、コンパイラーは、関数呼び出しをその関数にリンクするためにリンカーが使用できるクラフを生成します。 staticキーワードを使用すると、同じファイル内の他の関数がこの関数を呼び出すことができます(リンカーに頼らずに実行できるため)が、リンカーには他のファイルが関数にアクセスする情報がありません。

26
3Doubloons

上記の投稿を見て、1つの詳細を指摘したいと思います。

メインファイル( "main.c")が次のようになっているとします。

#include "header.h"

int main(void) {
    FunctionInHeader();
}

次に、3つのケースを考えます。

  • ケース1:ヘッダーファイル( "header.h")は次のようになります。

    #include <stdio.h>
    
    static void FunctionInHeader();
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }
    

    次に、Linuxで次のコマンドを実行します。

    gcc main.c header.h -o main
    

    成功します! 1つを実行する場合、それに続いて

    ./main
    

    出力は

    ヘッダー内の関数の呼び出し

    これは、静的関数が出力するものです。

  • ケース2:ヘッダーファイル( "header.h")は次のようになります。

    static void FunctionInHeader();     
    

    また、もう1つのファイル「header.c」があり、次のようになります。

    #include <stdio.h>
    
    #include "header.h"
    
    void FunctionInHeader() {
        printf("Calling function inside header\n");
    }
    

    その後、次のコマンド

    gcc main.c header.h header.c -o main
    

    エラーが発生します。

  • ケース3:

    ケース2と同様ですが、ヘッダーファイル( "header.h")が次のようになっています。

    void FunctionInHeader(); // keyword static removed
    

    その後、ケース2と同じコマンドが成功し、さらに./mainを実行すると、期待される結果が得られます。

したがって、これらのテスト(Acer x86マシン、Ubuntu OSで実行)から、

staticキーワードは、関数が宣言されている場所とは別のファイルで定義されるのを防ぎます。

間違っている場合は修正してください。

8
mercury0114

Cプログラマは、JavaおよびC++でパブリックおよびプライベート宣言を使用するのと同様に、静的属性を使用してモジュール内の変数および関数宣言を非表示にします。 Cソースファイルはモジュールの役割を果たします。静的属性で宣言されたグローバル変数または関数は、そのモジュールに対してプライベートです。同様に、静的属性なしで宣言されたグローバル変数または関数はすべてパブリックであり、他のモジュールからアクセスできます。可能な限り静的属性を使用して変数と関数を保護することをお勧めします。

5

pmgの答えは非常に説得力があります。オブジェクトレベルで静的宣言がどのように機能するかを知りたい場合は、以下の情報がおもしろいかもしれません。 pmgで記述された同じプログラムを再利用し、.so(共有オブジェクト)ファイルにコンパイルしました

次の内容は、.soファイルを何かにダンプした後のものですhum​​an readable

0000000000000675 f1f1関数のアドレス

000000000000068c f2f2(staticc)関数のアドレス

関数アドレスの違いに注意してください、それは何かを意味します。別のアドレスで宣言された関数の場合、f2が非常に遠くにあるか、オブジェクトファイルの別のセグメントにあることを十分に示すことができます。

リンカは、PLT(プロシージャリンケージテーブル)およびGOT(グローバルオフセットテーブル)と呼ばれるものを使用して、リンクするためにアクセスできるシンボルを理解します。

今のところ、GOTとPLTがすべてのアドレスを魔法のようにバインドし、動的セクションがリンカーによって表示されるこれらすべての関数の情報を保持すると考えてください。

.soファイルの動的セクションをダンプした後、多数のエントリを取得しますが、f1およびf2関数にのみ関心があります。

動的セクションは、f1関数のアドレス00000000000675のみのエントリを保持し、f2のエントリは保持しません!

Num:値サイズタイプバインドVis Ndx名

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

以上です !。このことから、.soファイルの動的セクションにないため、リンカーがf2関数を見つけることに失敗することは明らかです。

2
human.js