2013-09-06 4 views
7

静的と宣言された関数と変数は内部リンケージを持ち、ファイルスコープを持ち、他のファイルの関数からは見えません。C-静的宣言された関数のリンク

は、私はこのような機能を宣言することとします - FILE1.Cという名前の1つのファイルに

static int foo(int i); 

私が使用ポインタを介して他のファイルfile2.cからこの機能にアクセスすることができます。

私はそれができると書かれているが、どのようにそれが可能かわからない本を通っている。

そして、これらは、正確な線です: - 。それは内部リンケージを持っているので、fooはそれが定義されていているファイルの外 から直接呼び出すことはできません

(静的であるとのfooの宣言んではない それが別のファイルで呼び出されることを完全に防止します;間接的な関数ポインタによる呼び出しも可能です)。

+1

他のコンパイル単位から関数にアクセスしたい場合は、なぜ最初に静的にしていますか? – Jon

+0

@Jon:コールバックを静的なものとして宣言することができます(他の誰もその識別子で関数を呼び出すことはできません)。あなたはそれをコールバック関数にする必要があり、通常のものと同じように呼び出すことはできません。 – Gauthier

+0

@Gauthier:その場合、アドレスを呼び出し先に渡すだけでは何もする必要はありません。 'takes_callback(foo)' - あなたはすでにそれを提案しているのです。 OPは、(それを見たと答えた人たちのように)グローバルにアクセス可能な関数ポインタを求めていますが、これはまったく意味がありません。もちろん、moar rep * *の文字通りの質問に人々が答えるのを妨げることはありません。... – Jon

答えて

4

多分あなたはこれをお探しですか?ここで

// file1.h 
extern void (*fptr)(void); 

// file1.c 
static void foo(void) 
{ 
    printf("Hello world!\n"); 
} 

void (*fptr)(void) = &foo; //or void (*fptr)(void) = foo; 

// file2.c 
#include "file1.h" 

fptr(); 

fooは静的であるが、そのアドレスは非staticグローバル変数を介して参照されます。それは完全に可能です。

2

関数は別のファイルで静的なので他のファイルでは名前で呼び出すことはできませんが、関数ポインタを使用して関数を呼び出すことができます。

extern int (*pf)(int);

あなたはこのポインタにfooを割り当てる必要があり、その後、あなたはそれにアクセスすることができます。

1

H2CO3はあなたに正しい答えを与え、別の方法:要するに

/* a.h */ 
typedef int (*fptr)(int); 

fptr call_foo(void); 

/* a.c */ 
#include "a.h" 

static int foo(int i) 
{ 
    return i * 2; 
} 

fptr call_foo(void) 
{ 
    return foo; 
} 

/* main.c */ 
#include <stdio.h> 
#include "a.h" 

int main(void) 
{ 
    fptr fn = call_foo(); 

    printf("%d\n", fn(2)); 
    return 0; 
} 
0

:はいあなたはポインタを介し静的メソッドにアクセスすることができます。

これを理解するには、コンパイラのフードの中で何が起こっているのかをもう少し理解するのが最善でしょう。

わかりやすくするために、コンパイルされたプログラムはマシンコードで記述されています。 "プログラムローダー"用にコンパイルされたプログラムには特別な情報がありますが、プログラム自体はプロセッサーによって実行される命令です。

Cで関数 "foo()"を呼び出すと、Cコンパイラはこれを "CALL"プロセッサの動作に変換します。コードでは、fooのアドレス(文字通りメモリアドレスまたは「オフセット」)によってCALL操作が行われます。メモリアドレスであるため、名前( "foo")は使用されません。また、リンカーは、これが動作するために "foo"について知る必要はありません。

C言語で関数 "bar()"を呼び出し、その関数が別のコンパイル単位(別のCファイル)にあると、コンパイラはプログラム内のどこにメモリ)を呼び出します。つまり、CALL操作後にどのアドレスを書き込むのかはわかりません。これが起こると、アドレス用のスペースを残すコードが書き込まれますが、リンカーのメモが残ります。ノートはリンカーに "ここにバーのアドレスを置く"と伝えます。リンカは、メモリアドレスを使用して書かれたプログラムを修正します。リンカーがこれを行えるようにするには。コンパイラは、すべての関数名とそれぞれのアドレスを含むテーブルをコードに書き込みます。

静的なことは何ですか?これは単に、リンカーに渡されるテーブルに関数の名前とアドレスを書き込まないようにコンパイラに指示します。この関数は関数としてコードにはまだ存在しますが、リンカーはどこにあるのかわかりません。同じコンパイル単位内のコードは、その関数がどこにあるかを知るでしょう。したがって、同じコンパイル単位内の関数は、関数のアドレスをコンパイル単位外のポインタとして渡すことができます。

file1.h

typedef void (* VoidFunctionPointer)(); 

extern VoidFunctionPointer somethingInteresting; 

bar(); 

FILE1.C

#include "a.h" 
VoidFunctionPointer somethingInteresting; 

static void foo() { 
    // do something interesting; 
} 


void bar() { 
    // we know what foo is because we're in the same compilation unit 
    somethingInteresting = foo; 
} 

file2.c

#include "a.h" 

int main(int argC, char ** argV) { 
     bar(); 
     // we can call somethingInteresting as if it is a function no matter 
     // wether it's declared static in code or not 
     // this can be foo, just as long as we don't have to use the name "foo" 
     somethingInteresting(); 
} 

関数ポインタを渡すために使用するCコードは次のようなものです

このコードファイル2実際にはfile1から静的関数を実行します。重要なのは、file2はその関数の名前を必要としないので、staticは関数ポインタより効果がないということです。私はPE形式のMicrosoftの説明を読んだお勧めすることができます

[ここ](.EXEや.DLL):私が使用可能であると思われ、それを行うための

http://msdn.microsoft.com/en-us/library/ms809762.aspx

+0

file2が名前を必要としないという事実は真ですが、強力なのは、誰かに伝える必要なしに、file1が 'somethingInteresting'が指すどの機能を変更できるかということです。 – Gauthier

+0

@Gauthierはい正しいですが、関数ポインタは単純に 'static'制限を回避するためのものではありません。それは単なる副作用です。 –

1

さらに別の方法は、作ることです静的関数はコールバックです。ここで

//------------------------------ 
// file react.c 
#include "event.h" 

static void react_on_event_A(void) 
{ 
    // do something if event_A occurs. 
} 

void react_init(void) 
{ 
    event_setup(EVENT_A, react_on_event_A); 
} 

あなたはイベントドライバがに呼び出すことができるコールバックとしてreact_on_event_A機能を設定するが、その識別子で関数を呼び出すから他の誰をブロックしています。あなたは本当に他の人にその機能を止めるように言っています。

イベントドライバがこのようになります。

//------------------------------ 
// file event.h 
typedef enum { 
    EVENT_A, 
} event_t; 

void event_setup(event_t event, void (*callback)(void)); 
void event_check_and_run(void); 


//------------------------------ 
// file event.c 
static void (*event_A_callback)(void); 

void event_setup(event_t event, void (*callback)(void)) 
{ 
    if (event == EVENT_A) { 
     event_A_callback = callback; 
    } 
} 

// Scheduled periodically, or reacting on interrupt. 
void event_check_and_run(void) 
{ 
    if (occured(EVENT_A) && (event_A_callback != NULL)) { 
     event_A_callback(); 
    } 
} 

そうすることの利点は、他のモジュール(この場合event)は、独自の静的関数へのアクセスを有するモジュールreact制御することです。

他の選択肢(機能をstaticにするか、またはヘッダーファイルにポインタを公開する)では、誰にも誰にでもアクセスできます。

+0

+1、非常に面白い –