2009-12-15 9 views
12

Napotkałem działający (z kompilatorami XLC8 i MSFT9) fragment kodu, zawierający plik C++ z funkcją zdefiniowaną przez C linkage i argumentem referencyjnym. To powoduje błędy, ponieważ referencje dotyczą tylko C++. Dana funkcja jest wywoływana z kodu C, gdzie jest zadeklarowana jako przyjmująca argument wskaźnika do tego samego typu zamiast argumentu referencyjnego.Argument o przywołaniu C++ i C linkage

uproszczony przykład:

C++ plik:

extern "C" void f(int &i) 
{ 
    i++; 
} 

C plik:

void f(int *); 

int main() 
{ 
    int a = 2; 
    f(&a); 
    printf("%d\n", a); /* Prints 3 */ 
} 

Teraz Słowo na ulicy jest to, że większość C++ kompilatory, pod maską implementuj referencje tak jak wskaźnik. Czy tak jest, i czy tylko szczęście powód, dla którego ten kod działa lub czy mówi gdzieś w specyfikacji C++, jaki jest wynik, kiedy definiujesz funkcję z argumentem referencyjnym i łączem C? Nie byłem w stanie znaleźć żadnych informacji na ten temat.

+0

Z tego co widzę, sekcja 7.5 Standard C++ (specyfikacja sprzężenia) nie mówi nic na ten temat. –

Odpowiedz

4

Moja kopia n3000.pdf (z here) ma to powiedzieć w sekcji 7.5 — specyfikacje Szkielet:

. Powiązanie C++ z obiektami zdefiniowanymi w innych językach oraz z obiektami zdefiniowanymi jako w C++ z innych języków to implementacja zdefiniowana i zależna od języka. Tylko wtedy, gdy strategie rozmieszczania obiektów w obiekcie są podobne, można uzyskać takie połączenie.

Ponieważ C i C++ to różne języki, oznacza to, że nie można polegać na tej "funkcji" typowych kompilatorów.

Silniejszy jest uwaga 5 w tej samej części kopalni (nacisk):

Jeśli dwie deklaracje oświadczam FUNKCJE o tej samej nazwie i- parametru typu listy (8.3.5), aby być członkowie ta sama przestrzeń nazw lub zadeklaruj obiekty o tej samej nazwie do będące członkami tej samej przestrzeni nazw i deklaracje podają nazwy różnych powiązań językowych, program jest źle sformułowany; nie jest wymagana żadna diagnostyka , jeśli deklaracje są wyświetlane w różnych jednostkach tłumaczeniowych.

Tak, powiedziałbym, że to, co nie jest gwarantowane do pracy zgodnie z normą, a kompilator nie jest wymagane, aby wydrukować diagnostyczny dla przykładu dałeś ponieważ deklaracje są w różnych jednostkach tłumaczeniowych.

FYI, to "działa na mnie" z gcc i g ++ w wersji 4.2.1 na Snow Leopard.

+0

Całkowicie poprawny cytat, ale pamiętaj, że ma również swoją zaletę: ponieważ jest on zdefiniowany na etapie implementacji, jeśli działa, działa. – MSalters

+0

Tak, ale może działa, ponieważ działa z tą konkretną wersją kompilatora? Próbowałem przeglądając dokumentację gcc, ale nie mogłem znaleźć żadnych informacji. O ile ja to rozumiem, pytanie jest bardziej "teoretyczne", próbując dowiedzieć się, czy standard dopuszcza takie deklaracje. Wątpię, czy OP chce to zrobić w prawdziwym kodzie, biorąc pod uwagę, że tak łatwo jest tego uniknąć. –

+0

Kod jest w trakcie produkcji i działa, ale chciałbym zachować zgodność ze standardami kodowymi, ponieważ nowe platformy lub kompilatory mogą być celem w przyszłości. – olovb

11

W wielu przypadkach, ale nie we wszystkich, odniesienie może zostać zaimplementowane za pomocą wskaźnika "automatycznie dereferencji". To, że każdy konkretny kompilator traktuje funkcję z łączem C, a parametr referencyjny w ten sposób nie jest gwarantowany w standardzie C++ i powinieneś traktować go jako szczegół implementacji.

Nie jest trudno napisać funkcję przekierowania, które ma wskaźnik i wywołuje swoją funkcję, jeśli trzeba to zrobić bez opierając się na szczegółach realizacji:

void real_f(int& n) { 
    n++; 
} 
extern "C" void f(int* p) { // called from C code 
    real_f(*p); 
} 
3

referencyjnych jest alternatywna nazwa dla obiektu. Technicznie rzecz biorąc, nie ma niczego w C, które mogłoby bezpośrednio odwzorować odniesienie do C++.

Oczywistą implementacją odniesienia jest wskaźnik (stały), który jest dereferencjonowany za każdym razem, gdy jest używany. Tak więc, w zależności od tego, w jaki sposób Twój kompilator implementuje odniesienia, Twój kod może działać. Ale to nie jest poprawne.

Rozwiązaniem jest napisanie funkcji C, która odbiera obiekt rzeczywisty lub wskaźnik do tego obiektu i wywołuje z tego funkcję C++.

+1

Nie jest to gwarantowane, ale niekoniecznie jest nieprawidłowe. Jak inne języki nazywają funkcje C++ (w tym 'extern" C "' funkcje C++) jest poza zakresem standardu C++. Jeśli chodzi o C, funkcja została poprawnie zadeklarowana i wywołana. Jest to problem z implementacją i to, czy użytkownik może, powinien lub nie może na nim polegać, będzie się różnić w zależności od wymagań projektu. –

0

To co Bjarne Stroustrup ma do powiedzenia o referencje.

"Większość odniesień narzędzia są implementowane przy użyciu zmiennej wskaźnik, że to odniesienie zwykle zajmuje jedno słowo pamięci Jednak odniesienie że jest używany wyłącznie lokalnie - i często jest - eliminowany przez optymalizator ".

Na przykład:

struct S 
{ 
    int a; 
    int b[100]; 
}; // just an example 

void do_something(const vector<S>& v) 
{ 
    for (int i=0; i<v.size(); ++i) 
    { 
     int(&p)[100] = v[i].b; 
     for (int j=0; j<100; ++j) 
      cout <<p[j]; 
    } 
} 

W tym przypadku p nie musi być przechowywane w pamięci (może to po prostu nie istnieje w rejestrze, może zniknie w instrukcji).

+0

Masz na myśli 'int (& p) [100] = v [i] .b;'; także s /, /;/i v.size(). –

+0

@Roger: Dzięki, edytowałem swój wpis :) –

+4

Byłoby miło, gdybyś dał pełniejszy cytat. * Gdzie * tak powiedział Stroustrup? (Strona internetowa lub tytuł książki, edycja i numer strony) Z drugiej strony, * już wiemy * odniesienia są często realizowane jako wskaźniki. Dlatego kod w pytaniu działa tak jak obecnie. Nie widzę sensu, który próbowaliście osiągnąć za pomocą tej odpowiedzi, szczególnie w odniesieniu do parametrów funkcji z łączeniem C. –

1

Myślę, że masz już dobre odpowiedzi, więc po prostu wskaż coś ekstra na swoje pytanie.

Moje pokorne zrozumienie polega na tym, że osoba zewnętrzna tworzy jedynie symbol C.

Więc przykład jeszcze „wydaje się działać” (gcc/g ++), nawet kiedy dodać obiekt klasy - Musiałam spróbować IR w to uwierzyć:

class A {}; 

extern "C" void f(int &i, A a) { 
    i++; 
}