Cで「静的」とはどういう意味ですか?


1138

staticCコードのさまざまな場所で使用されている単語を見てきました。これはC#の静的関数/クラス(オブジェクト間で実装が共有されている)のようなものですか?



15
タイトルの最後、@ Lundinから「Cプログラム内」を削除する理由は何ですか?タグcが存在する場合は少し冗長ですが、タグを検査することなく、分類をよりすばやく確認できます。この冗長性は、静的検索やGoogle検索など、他の言語に関する質問も含まれている可能性がある方向から質問に到達したときに非常に快適です。
Palec

5
@Palecタグリストにあるアイテムはタイトルで重複するというSOポリシーがあります。サイトは実際のWebサイトにCを自動的に追加します。「C static」のグーグルはこの答えをトップヒットとしています。これが変更された理由は、この質問がSO C言語のFAQの一部になり、追加されたすべての投稿が少し洗練されたためです。
ランディン2017年

1
@Lundin SOはタイトルにタグを1つだけ追加するため(最も一般的ですか?)、タイトルに "C"を付けたままにします。ある日「構文」がCよりも多くの質問に達するとどうなりますか?私はむしろ、明示的な行動:-)編集使用したい:ああ、それ以外言っメタ質問があります:meta.stackexchange.com/questions/19190/...
チロSantilli冠状病毒审查六四事件法轮功

1
これは私がQuoraで見つけた説明です。間違いなく読む価値があります!
nalzok 2017

回答:


1520
  1. 関数内の静的変数は、呼び出し間でその値を保持します。
  2. 静的グローバル変数または関数は、それが宣言されているファイルでのみ「見られる」

(1)は、初心者の場合は外国のトピックなので、例を示します。

#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}

これは印刷します:

a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60

これは、関数が呼び出し間でいくつかの状態を保持する必要があり、グローバル変数を使用したくない場合に役立ちます。ただし、この機能は慎重に使用する必要があります。コードがスレッドセーフでなくなり、理解しにくくなります。

(2)「アクセス制御」機能として広く使用されています。いくつかの機能を実装する.cファイルがある場合、それは通常、いくつかの「パブリック」関数のみをユーザーに公開します。残りの機能はstatic、ユーザーがアクセスできないようにする必要があります。これはカプセル化です。

ウィキペディアの引用:

Cプログラミング言語では、静的変数をグローバル変数および関数と共に使用して、それらのスコープを包含ファイルに設定します。ローカル変数では、staticを使用して、自動的に割り当てられたメモリではなく、静的に割り当てられたメモリに変数を格納します。言語はどちらのタイプのメモリの実装も指示しませんが、静的に割り当てられたメモリは通常、コンパイル時にプログラムのデータセグメントで予約されますが、自動的に割り当てられたメモリは通常、一時的なコールスタックとして実装されます。

2番目の質問に答えると、C#とは異なります。

ただし、C ++では、staticクラス属性(同じクラスのすべてのオブジェクト間で共有)とメソッドを定義するためにも使用されます。Cではクラスがないため、この機能は関係ありません。


179
パックス、OPは静的について知らないので、コンパイルユニットとファイルの違いに彼を突っ込むことをお勧めしますか?:-)
Eli Bendersky、2009

138
コンパイル単位は、コンパイラが認識する単一のファイルです。.cファイルには他の.cファイルが含まれる場合がありますが、プリプロセッサーがインクルードを分類した後、コンパイラーは最終的に単一の「コンパイル単位」だけを認識します。
Eli Bendersky、2009

81
@robUK:コンパイラーは.hファイルについてさえ知りません-これらはプリプロセッサーで.cファイルに結合されます。つまり、すべてのヘッダーが含まれた.cファイルは単一のコンパイル単位であると言えます。
Eli Bendersky、2010

6
@TonyD多分それは混乱するかもしれませんが、それはコンパイルがどのように機能するかです。通常は1つ.cのヘッダーファイルの束ですが、悪魔はいつも典型的ではないものの中にあります。
peterph 2014

7
@TonyDコンパイラはコンパイルを行います。プリプロセッサは前処理を行います。ツールチェーンを「コンパイラ」と呼んでも、ツールチェーンの内容や機能は変わりません。
Miles Rout 2014

231

ここでカバーされていないもう1つの使用法があります。これは、関数への引数としての配列型宣言の一部です。

int someFunction(char arg[static 10])
{
    ...
}

このコンテキストでは、これは、この関数に渡される引数がchar少なくとも10個の要素を持つ型の配列でなければならないことを指定します。詳細については、こちらの質問を参照してください


3
Cに配列引数があるとは思いませんでしたか?Linus Torvaldsは、これをしている人々について怒りをもって怒っています。
suprjami 2015

13
@jamieb:Cには配列引数はありませんが、この特定の構文は、関数が値を持つことarg[0]を期待しarg[9]ていることを意味します(これは、関数がnullポインターを受け入れないことも意味します)。コンパイラーはこの情報を何らかの形で最適化に利用できます。静的アナライザーはこの情報を利用して、関数にnullポインター(または指定できるより少ない要素の配列)が渡されないようにします。
dreamlax 2015

19
@Qix-これは、staticC99 で与えられた新しいオーバーロードされた意味でした。それは10年半以上前のものですが、すべてのコンパイラー作成者がC99機能のすべてを採用しているわけではありません。そのため、C99は全体としてほとんど不明のままです。
Happy Green Kid Naps

@suprjami 「配列引数」の意味は100%わかりませんが、もしそうなら、それはC99で追加されint arr[n];VLA(可変長配列)です。それはあなたが意味したことですか?
ラスタジェダイ2016年

170

短い答え... それは異なります。

  1. 静的に定義されたローカル変数は、関数呼び出し間で値を失うことはありません。つまり、これらはグローバル変数ですが、スコープはそれらが定義されているローカル関数に限定されます。

  2. 静的グローバル変数は、それらが定義されているCファイルの外には表示されません。

  3. 静的関数は、それらが定義されているCファイルの外には表示されません。


8
「静的関数」と「プライベート関数」は同じ意味ですか?同様に、「静的グローバル変数」と「プライベートグローバル変数」は同じものですか?
user1599964 2013年

40
これは何の公開/非公開はC.ではありませんC.についてです
クリス

19
@ user1599964 privateC にはありませんが、類推は適切です。staticは、指定されたファイルに対して物事を「プライベート」にします。また、CのファイルはC ++のクラスにマップされることがよくあります。
Ciro Santilli冠状病毒审查六四事件法轮功

67

マルチファイル変数スコープの例

ここでは、staticが複数のファイルにわたる関数定義のスコープにどのように影響するかを示します。

交流

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/

/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/

/* OK: extern. Will use the one in main. */
extern int i;

/* OK: only visible to this file. */
static int si = 0;

void a() {
    i++;
    si++;
    puts("a()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

main.c

#include <stdio.h>

int i = 0;
static int si = 0;

void a();    

void m() {
    i++;
    si++;
    puts("m()");
    printf("i = %d\n", i);
    printf("si = %d\n", si);
    puts("");
}

int main() {
    m();
    m();
    a();
    a();
    return 0;
}

GitHubアップストリーム

コンパイルして実行:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o

出力:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

解釈

  • には2つの別々の変数がありsi、各ファイルに1つあります
  • 単一の共有変数があります i

いつものように、スコープが小さいほど良いので、static可能であれば常に変数を宣言してください。

Cプログラミングでは、ファイルは「クラス」を表すためによく使用され、static変数はクラスのプライベート静的メンバーを表します。

それについて規格が言うこと

C99 N1256ドラフト 6.7.1「ストレージクラス指定子」staticは、「ストレージクラス指定子」であると述べています。

6.2.2 / 3「識別子のリンケージ」は次のstaticことを意味すると言いますinternal linkage

オブジェクトまたは関数のファイルスコープ識別子の宣言にストレージクラス指定子staticが含まれている場合、識別子には内部リンクがあります。

6.2.2 / 2はinternal linkage、この例のように動作することを示しています。

プログラム全体を構成する翻訳単位とライブラリのセットでは、外部リンケージを含む特定の識別子の各宣言は、同じオブジェクトまたは関数を示します。1つの変換単位内で、内部リンケージを持つ識別子の各宣言は、同じオブジェクトまたは関数を示します。

ここで、「翻訳単位は前処理後のソースファイルです。

GCCがELF(Linux)用にどのように実装するのですか?

STB_LOCAL結合。

コンパイルすると:

int i = 0;
static int si = 0;

シンボルテーブルを次のように分解します。

readelf -s main.o

出力には以下が含まれます。

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
 10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

したがって、バインディングはそれらの間の唯一の重要な違いです。セクションValueへのオフセットにすぎない.bssため、異なることが予想されます。

STB_LOCALELF仕様のhttp://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.htmlに記載されています

STB_LOCALローカルシンボルは、それらの定義を含むオブジェクトファイルの外には表示されません。同じ名前のローカルシンボルが互いに干渉することなく複数のファイルに存在する可能性があります

表現するのに最適な選択staticです。

staticなしの変数はSTB_GLOBALであり、仕様には次のように記載されています。

リンカーが複数の再配置可能オブジェクトファイルを組み合わせる場合、同じ名前のSTB_GLOBALシンボルを複数定義することはできません。

これは、複数の非静的定義のリンクエラーと一貫しています。

を使用して最適化を開始する-O3と、siシンボルはシンボルテーブルから完全に削除されます。外部からは使用できません。TODO最適化がない場合に、シンボルテーブルに静的変数を保持する理由 それらは何にでも使用できますか?たぶんデバッグ用。

こちらもご覧ください

C ++の匿名の名前空間

C ++では、静的ではなく匿名の名前空間を使用することをお勧めします。これにより、同様の効果が得られますが、型定義がさらに非表示になります。名前のない名前空間と静的関数の比較


39

場合によります:

int foo()
{
   static int x;
   return ++x;
}

関数は1、2、3などを返します---変数はスタックにありません。

交流:

static int foo()
{
}

つまり、この関数はこのファイルでのみスコープを持っています。したがって、acとbcは異なるfoo()s を持つことができ、fooは共有オブジェクトに公開されません。したがって、fooをacで定義した場合b.c、他の場所から、または他の場所からアクセスすることはできません。

ほとんどのCライブラリでは、すべての「プライベート」関数は静的であり、ほとんどの「パブリック」は静的ではありません。


18
スタックまたはヒープ上にないxについて言及する場合は+1。それは静的メモリ空間にあります。
Gob00st

1
@ Gob00st静的メモリ空間?あなたは「データセグメント」を意味しました...?
Yousha Aleayoub 2018年

24

Cの「静的」には2つの意味があると人々は言い続けています。単一の意味を与える別の表示方法を提供します。

  • アイテムに「静的」を適用すると、そのアイテムに2つのプロパティが強制的に適用されます。(b)それは永続的です。

2つの意味があるように見える理由は、Cでは、「静的」が適用される可能性のあるすべての項目がすでにこれら2つのプロパティの1つを持っいるため、その特定の使用法はもう一方のみを含むかのようです。

たとえば、変数について考えます。関数の外部で宣言された変数はすでに(データセグメントで)永続性を持っているため、「静的」を適用すると、現在のスコープ(コンパイル単位)の外部でのみ変数が表示されなくなります。反対に、関数の内部で宣言された変数は、現在のスコープ(関数)の外ではすでに非表示になっているため、「静的」を適用しても永続化できます。

関数に「静的」を適用するのは、グローバル変数に適用するのと同じです。コードは必ず(少なくとも言語内で)永続的であるため、可視性のみを変更できます。

注:これらのコメントはCにのみ適用されます。C++では、クラスメソッドに「静的」を適用すると、キーワードに別の意味が与えられます。同様に、C99の配列引数拡張についても同様です。


(a)はせいぜい冗長です。スコープの外に見える変数はありません。それは単にスコープの定義です。つまり、C標準ではリンケージと呼ばれています。static得られる内部リンケージ識別子にします。
イェンス、

16

ウィキペディアから:

Cプログラミング言語では、静的変数をグローバル変数および関数と共に使用して、それらのスコープを包含ファイルに設定します。ローカル変数では、staticを使用して、自動的に割り当てられたメモリではなく、静的に割り当てられたメモリに変数を格納します。言語はどちらのタイプのメモリの実装も指示しませんが、静的に割り当てられたメモリは通常、コンパイル時にプログラムのデータセグメントで予約されますが、自動的に割り当てられたメモリは通常、一時的なコールスタックとして実装されます。


16

static 異なるコンテキストで異なることを意味します。

  1. C関数で静的変数を宣言できます。この変数は関数内でのみ表示されますが、一度だけ初期化され、その値を保持するという点でグローバルのように動作します。この例では、呼び出すたびにfoo()増加する数値が出力されます。静的変数は一度だけ初期化されます。

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
  2. staticのもう1つの用途は、関数またはグローバル変数を.cファイルに実装したが、そのシンボル.objがファイルによって生成された外部に表示されないようにする場合です。例えば

    static void foo() { ... }

8

静的な関数で変数を宣言した場合、その値は関数呼び出しスタックに格納されず、関数を再度呼び出すときにも使用できます。

グローバル変数を静的に宣言すると、そのスコープは宣言したファイル内に制限されます。これは、プログラム全体で読み取りおよび変更できる通常のグローバルよりも少し安全です。


8

私は古い質問に答えるのが嫌いですが、「Cプログラミング言語」のセクションA4.1でK&Rがどのように説明するかについて誰も言及しなかったと思います。

つまり、静的という用語は2つの意味で使用されます。

  1. 静的は2つのストレージクラスの1つです(もう1つは自動)。静的オブジェクトは、呼び出し間でその値を保持します。すべてのブロックの外側で宣言されたオブジェクトは常に静的であり、自動化することはできません。
  2. ただし、static キーワード(コード内でキーワードとして使用されることに重点が置かれている)を宣言とともに使用すると、そのオブジェクトの内部リンケージが得られるため、その翻訳単位内でのみ使用できます。ただし、キーワードが関数で使用されている場合は、オブジェクトのストレージクラスが変更されます(オブジェクトはとにかくその関数内でのみ表示されます)。staticの反対はexternキーワードで、オブジェクトに外部リンケージを与えます。

Peter Van Der Lindenは、「エキスパートCプログラミング」で次の2つの意味を述べています。

  • 関数内では、呼び出し間でその値を保持します。
  • 関数レベルで、このファイルでのみ表示されます。

3番目のストレージクラスregisterがあります。一部の人々は、mallocや友達から返されたストレージ用に、4番目のストレージクラスが割り当てられていることも主張しています。
Jens

@Jens 'register'はコンパイラーへのヒントにすぎません。Cソース内からレジスタストレージを強制することはできません。だから私はそれをストレージクラスとは考えません。
GermanNerd

1
@GermanNerd ストレージクラス指定子(C99 6.7.1ストレージクラス指定子)を明確に作成するregisterため、ISO C標準はあなたの見解と一致しません。また、単なるヒントではありません。たとえば、コンパイラがレジスタを割り当てるかどうかに関係なく、ストレージクラスを持つオブジェクトにアドレス演算子を適用することはできません。&register
イェンス、

@Jens&について思い出させてくれてありがとう。私はあまりにも多くのC ++を実行した可能性があります。 '指定子。したがって、残っている唯一のことは、アドレスを取得できないというソースコードレベルの制限です。ところで、この小さな議論のおかげで、Netbeansのバグを見つけました。私の最新の更新以降、新しいCプロジェクトではデフォルトでg ++ツールチェーンになっています。
GermanNerd

6

Cでは、staticには、その使用範囲に応じて2つの意味があります。グローバルスコープでは、オブジェクトがファイルレベルで宣言されている場合、そのオブジェクトはそのファイル内でのみ表示されます。

他のスコープでは、特定のスコープに入るさまざまな時間の間、その値を保持するオブジェクトを宣言します。たとえば、intがプロシージャ内で宣言されている場合:

void procedure(void)
{
   static int i = 0;

   i++;
}

「i」の値は、プロシージャの最初の呼び出しでゼロに初期化され、その後、プロシージャが呼び出されるたびに値が保持されます。「i」が出力された場合、0、1、2、3、...のシーケンスを出力します


5

関数の静的変数は、その関数への最初のエントリで初期化され、呼び出しが終了した後も持続することに注意することが重要です。再帰的な関数の場合、静的変数は1回だけ初期化され、すべての再帰的な呼び出しの間、および関数の呼び出しが終了した後でも持続します。

変数が関数の外で作成された場合、それはプログラマーが変数が宣言されたソースファイルでのみ変数を使用できることを意味します。


5

これをmytest.cファイルで宣言した場合:

static int my_variable;

その後、この変数はこのファイルからのみ表示できます。変数を他の場所にエクスポートすることはできません。

関数内で宣言すると、関数が呼び出されるたびに変数の値がその値を保持します。

静的関数はファイルの外部からエクスポートできません。そのため、*.cファイルでは、関数と変数を静的に宣言すると、それらが非表示になります。


4

Cの静的変数には、プログラムの存続期間があります。

関数で定義されている場合は、ローカルスコープがあります。つまり、これらの関数内でのみアクセスできます。静的変数の値は、関数呼び出し間で保持されます。

例えば:

void function()
{
    static int var = 1;
    var++;
    printf("%d", var);
}

int main()
{
    function(); // Call 1
    function(); // Call 2
}

上記のプログラムでvarは、データセグメントに格納されます。その寿命はCプログラム全体です。

関数呼び出し1のvar後、2にvarなります。関数呼び出し2の後、3 になります。

の値はvar、関数呼び出し間で破棄されません。

var非静的変数とローカル変数の間にある場合は、Cプログラムのスタックセグメントに格納されます。関数が戻った後、関数のスタックフレームが破棄されるため、の値varも破棄されます。

初期化された静的変数はCプログラムのデータセグメントに格納されますが、初期化されていない変数はBSSセグメントに格納されます。

静的に関するもう1つの情報:変数がグローバルで静的である場合、変数にはCプログラムの存続期間がありますが、ファイルスコープがあります。そのファイルでのみ表示されます。

これを試すには:

file1.c

static int x;

int main()
{
    printf("Accessing in same file%d", x):
}

file2.c

    extern int x;
    func()
    {
        printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
    }

run gcc -c file1.c

gcc -c file2.c

次に、以下を使用してそれらをリンクしてみます。

gcc -o output file1.o file2.o

xにはfile1.cのファイルスコープがあり、リンカーはfile2.cで使用される変数xへの参照を解決できないため、リンカーエラーが発生します。

参照:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack

私はそれが各関数呼び出しの後に失われないことを意味し、データが永続的であることを理解し、なぜしないstatic int var = 1;ものにするたびに値の背中を変更
イームズ

3

静的変数は関数で使用できる特別な変数であり、呼び出し間でデータを保存し、呼び出し間でそれを削除することはありません。例えば:

void func(){
    static int count; // If you don't declare its value, the value automatically initializes to zero
    printf("%d, ", count);
    ++count;
}

void main(){
    while(true){
        func();
    }
}

出力:

0、1、2、3、4、5、...


printf("%d, ", count); count++;`printf("%d、 "、count ++)に置き換えることができます(重要ではありません:P)。
ラスタジェダイ2016年

2

静的変数値は異なる関数呼び出し間で持続し、そのスコープはローカルブロックに制限されます。静的変数は常に値0で初期化されます


2

2つのケースがあります。

(1)宣言されたローカル変数static:スタックではなくデータセグメントに割り当てられます。関数を再度呼び出しても、その値は保持されます。

(2)宣言されたグローバル変数または関数static:コンパイル単位の外では見えない(つまり、リンク中のシンボルテーブルのローカルシンボル)。


1

静的変数には、スコープの外に出た後でも保持するという特性があります!したがって、静的変数は以前のスコープで以前の値を保持し、新しいスコープで再度初期化されることはありません。

たとえば、これを見てください-プログラムの実行中、静的int変数はメモリに残ります。変数が宣言された関数呼び出しが終了すると、通常の変数または自動変数は破棄されます。

#include<stdio.h> 
int fun() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

これは出力されます:1 2

静的であると宣言されたため、1がメモリに留まるため

静的変数(グローバル変数など)は、明示的に初期化されていない場合は0に初期化されます。たとえば、次のプログラムでは、xの値は0として出力されますが、yの値はゴミです。詳細はこちらをご覧ください。

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

これは出力します:0 [some_garbage_value]

これらは、初心者のために上記で説明されていない、私が見つけた主要なものです!


-1

Cプログラミングでstaticは、は有効期間と可視性の両方を制御する予約キーワードです。関数内で変数を静的として宣言すると、その変数は関数全体でのみ表示されます。この使用法では、この静的変数の存続期間は、関数呼び出し時に開始され、その関数の実行後に破棄されます。次の例をご覧ください。

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

上記のプログラムはこの出力を提供します:

First Counter Output = 1 
Second Counter Output = 1 

関数を呼び出すとすぐに、関数が初期化されるからcount = 0です。そして、counterFunctionそれを実行している間、それはカウント変数を破壊します。


2
>上記のプログラムは次の出力を提供します:最初のカウンター出力= 1 2番目のカウンター出力= 1 <真ではありません。静的変数は一度だけ初期化されます。したがって、出力は1、2のようになります。
GermanNerd
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.