2013-09-06 22 views
7

Funkcje i zmienne zadeklarowane jako statyczne mają powiązania wewnętrzne i mają zasięg pliku i nie są widoczne dla funkcji w innych plikach.C- Powiązanie funkcji zadeklarowanych statycznie

Załóżmy, że zadeklarować funkcję tak: -

static int foo(int i); 

w jednym pliku o nazwie file1.c Czy mogę uzyskać dostęp do tej funkcji z innego file2.c plików poprzez wskaźniki wykorzystania.

Przechodzę przez książkę, w której napisano, że można to zrobić, ale nie wiem, jak to jest możliwe.

A oto dokładne linie: -.

Ponieważ ma wewnętrzne powiązania, foo nie może być wywołana bezpośrednio z poza pliku, w którym jest zdefiniowana (Deklarowanie foo być statyczna nie całkowicie uniemożliwić wywołanie go w innym pliku, pośrednie wywołanie za pomocą wskaźnika funkcji jest nadal możliwe).

+1

Jeśli chcesz uzyskać dostęp do funkcji z innych jednostek kompilacji, dlaczego robisz to "statyczne" w pierwszej kolejności? – Jon

+0

@Jon: można zadeklarować wywołanie zwrotne jako statyczne (nikt nie może wywołać funkcji po jego identyfikatorze) i wysłać jego wskaźnik do funkcji konfiguracji, aby zareagować na zdarzenie. Zmuszasz go do funkcji wywołania zwrotnego, a nie do wywoływania jako zwykły. – Gauthier

+0

@Gauthier: W takim przypadku nie musisz robić nic więcej niż po prostu przekazać adres adresatowi: 'takes_callback (foo)' - Widzę, że już to zasugerowałeś. OP pyta (tak jak ja i inni, którzy odpowiedzieli, że to widzimy) na globalnie dostępny wskaźnik funkcji, który jest całkowicie bezcelowy. Oczywiście to nie przeszkadza ludziom w odpowiadaniu na dosłowne pytanie dotyczące moar rep * * ... – Jon

Odpowiedz

4

Może szukasz tego?

// 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(); 

Tutaj foo jest statyczna, ale jego adres jest określany przez nie- zmiennej globalnej static. I to jest całkowicie możliwe.

2

Funkcja nie może być wywołana z nazwy w żadnym innym pliku, ponieważ jest statyczna w innym pliku, ale można to zrobić za pomocą wskaźnika funkcji.

extern int (*pf)(int);

Musisz przypisać foo do tego wskaźnika, a następnie można go otworzyć.

1

H2CO3 daje właściwą odpowiedź, inny sposób:

/* 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

W skrócie: Tak można uzyskać dostęp za pomocą metod statycznych wskaźników.

Aby to zrozumieć, lepiej jest zrozumieć nieco więcej tego, co dzieje się pod maską kompilatora.

Dla przejrzystości, skompilowany program jest zapisany w kodzie maszynowym. W skompilowanym programie znajdują się dodatkowe informacje dotyczące "programu ładującego program", ale sam program to instrukcje, które musi wykonać procesor.

Po wywołaniu funkcji "foo()" w C, kompilator języka C przetłumaczy to na operację procesora "CALL". Operacja CALL jest wykonywana w kodzie przez adres foo (dosłownie adres pamięci lub "offset"). Zauważ, że ponieważ jest to adres pamięci, nazwa ("foo") nie jest używana. Zauważ też, że linker nie musi wiedzieć o "foo", aby to działało.

Gdy wywołujesz funkcję "bar()" w C, a funkcja znajduje się w innej jednostce kompilacji (innym pliku C), kompilator ma mały problem, ponieważ nie wie, gdzie w programie (gdzie in pamięć) funkcją jest wywoływanie. To znaczy, że nie wie, jaki adres wpisać po operacji CALL. Gdy tak się stanie, zapisuje kod pozostawiając miejsce na adres, ale pozostawia notatkę dla linkera. Nota mówi linkerowi "wpisz tutaj adres paska". Łącznik koryguje napisany program za pomocą adresu pamięci. Aby umożliwić linkerowi to zrobić; kompilator zapisuje tabelę zawierającą każdą nazwę funkcji i odpowiedni adres w kodzie.

Co robi statyczny? To po prostu mówi kompilatorowi, aby nie zapisywał nazwy i adresu dla funkcji w tabeli przekazywanej do łącznika. Funkcja nadal istnieje w kodzie jako funkcja, ale linker nie wie, gdzie on jest. Każdy kod w tej samej jednostce kompilacji będzie wiedział, gdzie jest ta funkcja. Każda funkcja w tej samej jednostce kompilacji może przekazać adres funkcji jako wskaźnik poza jednostką kompilacji.

kod C użyć, aby przekazać wskaźnik funkcji jest coś takiego:

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

W tym pliku kodu2 w efekcie uruchamia funkcję statyczną z pliku1. Klucz jest taki, że plik2 nigdy nie potrzebuje nazwy tej funkcji, więc statyczny nie ma wpływu na wskaźniki funkcji.

mogę polecić przeczytaniu opisu Microsoft formatu PE (EXE i DLL) [tutaj]:

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

+0

Fakt, że plik2 nigdy nie potrzebuje nazwy, jest prawdziwy, ale potężne jest to, że plik1 może zmienić, która funkcja "coś ciekawego" wskazuje, bez potrzeby komukolwiek. – Gauthier

+0

@Gauthier Tak, masz rację, wskaźniki funkcji nie służą po prostu do ominięcia ograniczenia "statycznego". To tylko efekt uboczny. –

1

Jeszcze innym sposobem, aby to zrobić, co moim zdaniem jest użyteczny, to aby twoja funkcja statyczna to oddzwanianie.

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

Tutaj konfigurowania funkcji react_on_event_A jako zwrotnego że kierowca zdarzenie może wywołać, ale blokowanie nikomu z wywołaniem funkcji przez jego identyfikator. Naprawdę mówisz komukolwiek, żeby nie wykonywał tej funkcji.

Kierowca zdarzenie może wyglądać następująco:

//------------------------------ 
// 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(); 
    } 
} 

Zaletą jest to, że robi tak Moduł react kontrole, jakie inne moduły (event w tym przypadku) mają dostęp do własnej funkcji statycznej.

Z innymi alternatywami (tworząc funkcję static lub nie, lub publikując wskaźnik w pliku nagłówkowym), dajesz dostęp albo do nikogo, albo do wszystkich.

+0

+1, bardzo interesujące –