Cで関数を静的にする意味は何ですか?
Cで関数を静的にする意味は何ですか?
回答:
関数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;
}
#include <helper_file.c>
ますか?その場合、単一の翻訳単位になると思います...
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
です。関数のプロトタイプは両方のソースファイルに存在します(ヘッダーファイルは必要ありません)。リンカーは関数を解決します。
pmgはカプセル化にスポットを当てています。関数を他の変換単位から隠す(またはそのため)だけでなく、関数static
を作成すると、コンパイラーの最適化が存在する場合にパフォーマンス上の利点が得られます。
static
関数は現在の変換単位の外部からは呼び出すことができないため(コードがそのアドレスへのポインターを取得しない限り)、コンパイラーは関数へのすべての呼び出しポイントを制御します。
これは、非標準のABIを使用したり、完全にインライン化したり、外部リンケージを使用する関数では不可能なその他の最適化をいくつでも自由に実行できることを意味します。
static
関数へのポインターが現在の変換単位をエスケープする場合、その関数は他の変換単位から直接呼び出すことができます。
static
C のキーワードは、コンパイルされたファイル(.hではなく.c)で使用されるため、関数はそのファイルにのみ存在します。
通常、関数を作成すると、コンパイラはリンカが関数呼び出しをその関数にリンクするために使用できるクリフトを生成します。staticキーワードを使用すると、同じファイル内の他の関数がこの関数を呼び出すことができます(リンカーを使用せずに実行できるため)。一方、リンカーには他のファイルが関数にアクセスするための情報がありません。
上記の投稿を見て、私は一つの詳細を指摘したいと思います。
メインファイル( "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
成功します!実行した場合、それに続いて
./main
出力は
ヘッダー内の関数の呼び出し
これは、静的関数が出力するものです。
ケース2:ヘッダーファイル( "header.h")は次のようになります。
static void FunctionInHeader();
また、次のような「header.c」ファイルも1つあります。
#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キーワードは、関数が定義されている場所とは別の* .cファイルで呼び出されるのを防ぎます。
私が間違っていたら訂正してください。
Cプログラマーは、JavaおよびC ++でパブリックおよびプライベート宣言を使用するのと同じように、静的属性を使用してモジュール内の変数および関数宣言を非表示にします。Cソースファイルはモジュールの役割を果たします。static属性で宣言されたグローバル変数または関数は、そのモジュール専用です。同様に、静的属性なしで宣言されたグローバル変数または関数はすべてパブリックであり、他のモジュールからアクセスできます。可能な限りstatic属性を使用して変数と関数を保護することは、プログラミングの習慣として適切です。
pmgの答えは非常に説得力があります。静的宣言がオブジェクトレベルでどのように機能するかを知りたい場合は、以下の情報が興味深いかもしれません。私はpmgで作成した同じプログラムを再利用し、それを.so(共有オブジェクト)ファイルにコンパイルしました
以下の内容は、.soファイルを人間が読める形式にダンプした後のものです
0000000000000675 f1:f1関数のアドレス
000000000000068c f2:f2(staticc)関数のアドレス
関数アドレスの違いに注意してください、それは何かを意味します。異なるアドレスで宣言された関数の場合、f2がオブジェクトファイルの別のセグメントまたは非常に離れた場所にあることを示すことができます。
リンカーは、PLT(プロシージャリンクテーブル)およびGOT(グローバルオフセットテーブル)と呼ばれるものを使用して、へのリンクにアクセスできるシンボルを理解します。
とりあえず、GOTとPLTがすべてのアドレスを魔法のようにバインドし、動的セクションがリンカから見えるこれらすべての関数の情報を保持していると考えます。
.soファイルの動的セクションをダンプした後、一連のエントリを取得しますが、関心があるのはf1およびf2関数のみです。
動的セクションは、アドレス0000000000000675のf1関数のエントリのみを保持し、f2のエントリは保持しません。
数値:値サイズタイプバインドVis Ndx名
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
以上です !。これから、リンカは.soファイルの動的セクションにないため、f2関数の検索に失敗することは明らかです。
一部の関数へのアクセスを制限する必要がある場合は、関数を定義および宣言するときにstaticキーワードを使用します。
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */