2013-02-20 15 views
13

Moje pytania:Funkcja równość wskaźnik w C

  1. Czy funkcja równość wskaźnik gwarantowana przez standard C?
  2. Jeśli odpowiedź (1) brzmi "tak". Czy tak jest, niezależnie od tego, jaki wskaźnik uzyskano w różnych końcowych jednostkach kompilacji (na przykład główny plik wykonywalny i biblioteka współdzielona)?
  3. Jak radzi sobie z tym ładowarka dynamiczna? (Mogę wymyślić kilka powodów, dla których może to być trudne, wszystkie związane z kodem PIC (np. Tabele GOT w elf i jakikolwiek równoważny COFF używa do tego)). Niezależnie od (1) i (2) program ładujący linuksa wydaje się to gwarantować.

Oto przykład. Powyższe pytania sprowadzają się do tego, czy C gwarantuje, co main.c drukuje: "Function equality: 1" lub "Function equality: 0" oraz, w pierwszym przypadku, w jaki sposób ładuje się dynamiczny program ładujący.

common.h:

extern void * getc_main; 
extern void * getc_shared; 
void assign_getc_shared(); 

main.c:

#include <stdio.h> 
#include "common.h" 

int main() 
{ 
    getc_main = (void*) getc; 
    assign_getc_shared(); 
    printf("Function equality: %d\n", getc_main == getc_shared); 
    return 0; 
} 

shared.c:

#include <stdio.h> 
#include "common.h" 

void assign_getc_shared() 
{ 
    getc_shared = (void*) getc; 
} 

W Unix to będzie przygotowana z następujących poleceń:

cc -shared -fPIC -o libshared.so shared.c 
cc -o main main.c -L. -lshared 

i wykonane z:

LD_LIBRARY_PATH=. ./main 
+0

To dość długa droga, by zapytać "czy gwarantuje się, że standardowe funkcje biblioteczne zostaną uwzględnione tylko raz w pliku wykonywalnym" –

+0

. I myślę, że odpowiedź na pytanie pana Listera brzmi: "Nie, to nie jest gwarantowane" .Funkcje mogą być na przykład wstawiane - i jeśli wybierzesz adres funkcji inline, zostanie ona włączona do kodu jako "rzeczywista" funkcja, co oznacza, że ​​potencjalnie będzie wiele funkcji dla tej samej funkcji źródłowej. –

+0

@MrLister Gdybym był zainteresowany, aby wiedzieć tylko to, wtedy bym to tylko poprosił. Powodem zadawania dodatkowych pytań jest to, że jestem zainteresowany poznaniem szczegółów, jak dynamiczny program ładujący radzi sobie z tym problemem. Z twojego komentarza chyba nie jesteś i to w porządku. – fons

Odpowiedz

12

C 2011 (Komitet N1570 Projekt) 6.5.9 6: „Dwa wskaźniki porównać równe wtedy i tylko wtedy, gdy ... oba są wskaźniki do tego samego ... ... funkcji. Tak, tak, dwa wskaźniki do tej samej funkcji są równe.

Gdy adres funkcji jest wykonywany w dwóch różnych modułach obiektów, kompilator umieszcza symbol zastępczy w kodzie obiektowym. Ten symbol zastępczy jest wypełniany, gdy moduły obiektów są połączone w plik wykonywalny lub połączone z biblioteką dynamiczną w czasie wykonywania.

W przypadku bibliotek dynamicznych program ładujący dynamicznie wypełnia wszystkie obiekty zastępcze w pliku wykonywalnym, jeśli to konieczne, lub adres każdej funkcji jest w rzeczywistości lokalizacją kodu pośredniczącego, który przeskakuje do rzeczywistej funkcji, oraz elementem zastępczym w tym lub używanym przez tę funkcję. Kod pośredniczący jest wypełniany przez dynamiczny program ładujący.

Należy również pamiętać, że plik wykonywalny może zawierać więcej niż jedną instancję funkcji. Kompilator może wstawiać funkcję inline w kilku miejscach lub może, ze względu na swoje własne, zawierać specjalizację funkcji, jak również ogólną wersję. Jednakże, gdy adres funkcji jest zajęty, kompilator musi podać adres jednej ogólnej wersji. (Lub kompilator musi upewnić się, że program zachowuje się tak, jakby to było zrobione. Np. Jeśli kompilator może wykryć, że program nie porównuje wskaźników, może teoretycznie użyć innego adresu dla niektórych wystąpień adresu funkcji.)

+0

Oznacza to, że niemożliwe jest wdrożenie kompilatora C zgodnego z C 2011 w trybie rzeczywistym 80x86. Dostęp do dowolnego punktu pamięci można uzyskać za pośrednictwem 4096 różnych (daleko/ogromnych) wskaźników. –

+4

@tristopia: Nie rozumiem, jak wyciąga się wniosek. Fakt, że każdy adres ma 4096 potencjalnych reprezentacji, nie uniemożliwia kompilatorowi zapewnienia, że ​​różne reprezentacje tego samego adresu są takie same. Kompilator nie musi implementować 'a == b' z pojedynczą instrukcją; można dowolnie wykonywać arytmetykę, aby przekonwertować każdy z "a" i "b" z dowolnego formatu na pełny unikatowy adres, a następnie porównać uzyskane pełne adresy. –

+3

Dodatkowo, kompilator ma kontrolę nad przyjmowaniem adresów, więc może zapewnić, że konkretna reprezentacja adresu funkcji jest używana, a inne nie. –