web-dev-qa-db-ja.com

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

Cのコードのさまざまな場所でWordのstaticが使われています。これはC#の静的関数/クラスのようなものですか(実装はオブジェクト間で共有されます)。

976
David
  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 はグローバル変数と関数と共に使用され、それらのスコープをを含むファイルに設定します。ローカル変数では、 staticを使用して、変数を自動的に割り当てられたメモリではなく、静的に割り当てられたメモリに格納します。言語はどちらのタイプのメモリの実装も規定していませんが、静的に割り当てられたメモリは通常コンパイル時にプログラムのデータセグメントに予約されています。自動的に割り当てられたメモリは通常一時的な呼び出しスタックとして実装されます。

詳細については here および here を参照してください。

そして、あなたの2番目の質問に答えると、それはC#のようではありません。

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

1328
Eli Bendersky

ここでは扱われていないもう1つの用途があります。これは、関数への引数としての配列型宣言の一部としてです。

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

この文脈では、これは、この関数に渡される引数が、少なくとも10個の要素を含むchar型の配列でなければならないことを指定します。詳細については私の質問を参照してください ここ

209
dreamlax

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

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

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

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

149
cmcginty

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

ここでは、staticが複数のファイルにわたる関数定義の範囲にどのように影響するかを説明します。

交流

#include <stdio.h>

/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
https://stackoverflow.com/questions/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

解釈

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

いつものように、範囲が小さければ小さいほどよいので、可能であれば変数を常にstatic宣言してください。

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

それについての規格は何を言っている

C99 N1256 draft 6.7.1 "記憶クラス指定子"はstaticが "記憶クラス指定子"であることを示しています。

6.2.2/3 "識別子の結合"はstaticinternal linkageを意味すると言っています:

オブジェクトまたは関数のファイル有効範囲識別子の宣言に記憶域クラス指定子staticが含まれている場合、その識別子は内部リンケージを持ちます。

そして6.2.2/2はinternal linkageはこの例のように振る舞うと言っています:

プログラム全体を構成する翻訳単位およびライブラリーのセットでは、外部リンケージを使用した特定のIDの各宣言は、同じオブジェクトまたは機能を表します。 1つの翻訳単位内で、内部リンケージを持つIDの各宣言は、同じオブジェクトまたは機能を表します。 

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

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_LOCALは、ELF仕様の http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :に記載されています。

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

これはstaticを表すのに最適な選択です。

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

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

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

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

も参照のこと。

C++の匿名ネームスペース

C++では、staticの代わりに無名の名前空間を使用することをお勧めします。これにより、同様の効果が得られますが、型定義はさらに隠されます。 無名/無名の名前空間と静的関数

場合によります:

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

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

交流:

static int foo()
{
}

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

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

34
Artyom

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

  • アイテムに「静的」を適用すると、そのアイテムには2つのプロパティが強制されます。(a)現在のスコープ外では表示されません。 (b)永続的です。

2つの意味があるように思われる理由は、Cでは、「静的」が適用される可能性のあるすべての項目すでにこれらの2つのプロパティのいずれかを持っていますなので、seems asその特定の使用法が他方のみを含む場合。

たとえば、変数について考えます。関数の外部で宣言された変数には、すでに永続性があります(データセグメント内)ので、 'static'を適用すると、現在のスコープ(コンパイルユニット)の外では見えなくなります。反対に、関数の内部で宣言された変数は、現在のスコープ(関数)の外側で既に非可視性を持っているため、 'static'を適用すると、それらを永続的にすることができます。

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

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

21
PMar

ウィキペディアより

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

12
OscarRyz

staticは異なる文脈では異なることを意味します。

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

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

    static void foo() { ... }
    
12
m-sharp

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

手短に言うと、Word staticは two の意味で使用されます。

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

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

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

関数をstaticで宣言すると、その値は関数呼び出しスタックに格納されず、関数を再度呼び出すときにも使用できます。

グローバル変数をstaticと宣言した場合、その有効範囲は宣言したファイル内に限定されます。これは、プログラム全体を通して読んだり変更したりできる通常のグローバルよりも少し安全です。

5
Sam Hoice

Cでは、staticはその使用範囲に応じて2つの意味を持ちます。グローバルスコープでは、オブジェクトがファイルレベルで宣言されている場合、そのオブジェクトはそのファイル内でしか見えません。

それ以外のスコープでは、特定のスコープに入ったときの間で値を保持するオブジェクトを宣言します。たとえば、intがプロシージャ内でデカレートされているとします。

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

   i++;
}

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

5
Gary

これをmytest.cファイルで宣言すると、

static int my_variable;

この変数はこのファイルからのみ見ることができます。変数は他の場所にエクスポートすることはできません。

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

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

4
ant2009

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

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

3
Starhowl

Cの静的変数はプログラムの存続期間を持ちます。

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

例えば:

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

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

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

関数呼び出し1の後、varは2になります。関数呼び出し2の後、varは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
3
Sahil Manchanda

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

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、...

2
Yagel

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

1
Jonathon

2つの場合があります。

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

(2)宣言されたグローバル変数または関数static:コンパイル単位の外側には表示されません(つまり、リンク中のシンボルテーブル内のローカルシンボルです)。

1
Jonny Kong

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; 
}

上記のプログラムは私達にこの出力を与えるでしょう:
最初のカウンタ出力= 1 
秒カウンタ出力= 1 
関数を呼び出すとすぐにcount = 0が初期化されるので、counterFunctionを実行している間はcount変数が破棄されます。

0
Makdia Hussain