static
Cコードのさまざまな場所で使用されている単語を見てきました。これはC#の静的関数/クラス(オブジェクト間で実装が共有されている)のようなものですか?
static
Cコードのさまざまな場所で使用されている単語を見てきました。これはC#の静的関数/クラス(オブジェクト間で実装が共有されている)のようなものですか?
回答:
(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ではクラスがないため、この機能は関係ありません。
.c
のヘッダーファイルの束ですが、悪魔はいつも典型的ではないものの中にあります。
ここでカバーされていないもう1つの使用法があります。これは、関数への引数としての配列型宣言の一部です。
int someFunction(char arg[static 10])
{
...
}
このコンテキストでは、これは、この関数に渡される引数がchar
少なくとも10個の要素を持つ型の配列でなければならないことを指定します。詳細については、こちらの質問を参照してください。
arg[0]
を期待しarg[9]
ていることを意味します(これは、関数がnullポインターを受け入れないことも意味します)。コンパイラーはこの情報を何らかの形で最適化に利用できます。静的アナライザーはこの情報を利用して、関数にnullポインター(または指定できるより少ない要素の配列)が渡されないようにします。
static
C99 で与えられた新しいオーバーロードされた意味でした。それは10年半以上前のものですが、すべてのコンパイラー作成者がC99機能のすべてを採用しているわけではありません。そのため、C99は全体としてほとんど不明のままです。
int arr[n];
たVLA(可変長配列)です。それはあなたが意味したことですか?
短い答え... それは異なります。
静的に定義されたローカル変数は、関数呼び出し間で値を失うことはありません。つまり、これらはグローバル変数ですが、スコープはそれらが定義されているローカル関数に限定されます。
静的グローバル変数は、それらが定義されているCファイルの外には表示されません。
静的関数は、それらが定義されているCファイルの外には表示されません。
private
C にはありませんが、類推は適切です。staticは、指定されたファイルに対して物事を「プライベート」にします。また、CのファイルはC ++のクラスにマップされることがよくあります。
マルチファイル変数スコープの例
ここでは、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;
}
コンパイルして実行:
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
、各ファイルに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_LOCAL
ELF仕様のhttp://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.htmlに記載されています。
STB_LOCALローカルシンボルは、それらの定義を含むオブジェクトファイルの外には表示されません。同じ名前のローカルシンボルが互いに干渉することなく複数のファイルに存在する可能性があります
表現するのに最適な選択static
です。
staticなしの変数はSTB_GLOBAL
であり、仕様には次のように記載されています。
リンカーが複数の再配置可能オブジェクトファイルを組み合わせる場合、同じ名前のSTB_GLOBALシンボルを複数定義することはできません。
これは、複数の非静的定義のリンクエラーと一貫しています。
を使用して最適化を開始する-O3
と、si
シンボルはシンボルテーブルから完全に削除されます。外部からは使用できません。TODO最適化がない場合に、シンボルテーブルに静的変数を保持する理由 それらは何にでも使用できますか?たぶんデバッグ用。
こちらもご覧ください
static
関数に類似:https : //stackoverflow.com/a/30319812/895245static
してくださいextern
:externを使用してソースファイル間で変数を共有するにはどうすればよいですか?C ++の匿名の名前空間
C ++では、静的ではなく匿名の名前空間を使用することをお勧めします。これにより、同様の効果が得られますが、型定義がさらに非表示になります。名前のない名前空間と静的関数の比較
場合によります:
int foo()
{
static int x;
return ++x;
}
関数は1、2、3などを返します---変数はスタックにありません。
static int foo()
{
}
つまり、この関数はこのファイルでのみスコープを持っています。したがって、acとbcは異なるfoo()
s を持つことができ、fooは共有オブジェクトに公開されません。したがって、fooをacで定義した場合b.c
、他の場所から、または他の場所からアクセスすることはできません。
ほとんどのCライブラリでは、すべての「プライベート」関数は静的であり、ほとんどの「パブリック」は静的ではありません。
Cの「静的」には2つの意味があると人々は言い続けています。単一の意味を与える別の表示方法を提供します。
2つの意味があるように見える理由は、Cでは、「静的」が適用される可能性のあるすべての項目がすでにこれら2つのプロパティの1つを持っているため、その特定の使用法はもう一方のみを含むかのようです。
たとえば、変数について考えます。関数の外部で宣言された変数はすでに(データセグメントで)永続性を持っているため、「静的」を適用すると、現在のスコープ(コンパイル単位)の外部でのみ変数が表示されなくなります。反対に、関数の内部で宣言された変数は、現在のスコープ(関数)の外ではすでに非表示になっているため、「静的」を適用しても永続化できます。
関数に「静的」を適用するのは、グローバル変数に適用するのと同じです。コードは必ず(少なくとも言語内で)永続的であるため、可視性のみを変更できます。
注:これらのコメントはCにのみ適用されます。C++では、クラスメソッドに「静的」を適用すると、キーワードに別の意味が与えられます。同様に、C99の配列引数拡張についても同様です。
static
得られる内部リンケージ識別子にします。
static
異なるコンテキストで異なることを意味します。
C関数で静的変数を宣言できます。この変数は関数内でのみ表示されますが、一度だけ初期化され、その値を保持するという点でグローバルのように動作します。この例では、呼び出すたびにfoo()
増加する数値が出力されます。静的変数は一度だけ初期化されます。
void foo ()
{
static int i = 0;
printf("%d", i); i++
}
staticのもう1つの用途は、関数またはグローバル変数を.cファイルに実装したが、そのシンボル.obj
がファイルによって生成された外部に表示されないようにする場合です。例えば
static void foo() { ... }
静的な関数で変数を宣言した場合、その値は関数呼び出しスタックに格納されず、関数を再度呼び出すときにも使用できます。
グローバル変数を静的に宣言すると、そのスコープは宣言したファイル内に制限されます。これは、プログラム全体で読み取りおよび変更できる通常のグローバルよりも少し安全です。
私は古い質問に答えるのが嫌いですが、「Cプログラミング言語」のセクションA4.1でK&Rがどのように説明するかについて誰も言及しなかったと思います。
つまり、静的という用語は2つの意味で使用されます。
static
キーワード(コード内でキーワードとして使用されることに重点が置かれている)を宣言とともに使用すると、そのオブジェクトの内部リンケージが得られるため、その翻訳単位内でのみ使用できます。ただし、キーワードが関数で使用されている場合は、オブジェクトのストレージクラスが変更されます(オブジェクトはとにかくその関数内でのみ表示されます)。staticの反対はextern
キーワードで、オブジェクトに外部リンケージを与えます。Peter Van Der Lindenは、「エキスパートCプログラミング」で次の2つの意味を述べています。
register
ため、ISO C標準はあなたの見解と一致しません。また、単なるヒントではありません。たとえば、コンパイラがレジスタを割り当てるかどうかに関係なく、ストレージクラスを持つオブジェクトにアドレス演算子を適用することはできません。&
register
Cでは、staticには、その使用範囲に応じて2つの意味があります。グローバルスコープでは、オブジェクトがファイルレベルで宣言されている場合、そのオブジェクトはそのファイル内でのみ表示されます。
他のスコープでは、特定のスコープに入るさまざまな時間の間、その値を保持するオブジェクトを宣言します。たとえば、intがプロシージャ内で宣言されている場合:
void procedure(void)
{
static int i = 0;
i++;
}
「i」の値は、プロシージャの最初の呼び出しでゼロに初期化され、その後、プロシージャが呼び出されるたびに値が保持されます。「i」が出力された場合、0、1、2、3、...のシーケンスを出力します
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プログラムの存続期間がありますが、ファイルスコープがあります。そのファイルでのみ表示されます。
これを試すには:
static int x;
int main()
{
printf("Accessing in same file%d", x):
}
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への参照を解決できないため、リンカーエラーが発生します。
参照:
static int var = 1;
ものにするたびに値の背中を変更
静的変数は関数で使用できる特別な変数であり、呼び出し間でデータを保存し、呼び出し間でそれを削除することはありません。例えば:
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)。
静的変数には、スコープの外に出た後でも値を保持するという特性があります!したがって、静的変数は以前のスコープで以前の値を保持し、新しいスコープで再度初期化されることはありません。
たとえば、これを見てください-プログラムの実行中、静的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]
これらは、初心者のために上記で説明されていない、私が見つけた主要なものです!
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
それを実行している間、それはカウント変数を破壊します。