2009-12-15 17 views
12

Ho riscontrato un pezzo di codice funzionante (con compilatori XLC8 e MSFT9), contenente un file C++ con una funzione definita con collegamento C e un argomento di riferimento. Questo mi infastidisce, dato che i riferimenti sono solo in C++. La funzione in questione è chiamata dal codice C, dove viene dichiarata come se stesse prendendo un argomento puntatore allo stesso tipo al posto dell'argomento di riferimento.Argomento per riferimento C++ e collegamento C

esempio semplificato:

C++ file di:

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

file C:

void f(int *); 

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

Ora, la parola sulla strada è che la maggior parte dei compilatori C++, sotto il cofano, implementa i riferimenti proprio come un puntatore. È così e solo per pura fortuna il motivo per cui funziona questo codice o si dice da qualche parte nelle specifiche C++ quale sia il risultato quando si definisce una funzione con un argomento di riferimento e un collegamento C? Non sono stato in grado di trovare alcuna informazione su questo.

+0

Da quello che posso vedere la sezione 7.5 della Lo standard C++ (Linkage Specification) non dice nulla al riguardo. –

risposta

4

La mia copia di n3000.pdf (da here), ha questo da dire nella sezione 7.5 — specifiche Linkage:

. Il collegamento da C++ agli oggetti definiti in in altri linguaggi e agli oggetti definiti in C++ da altre lingue è definito dall'implementazione e dipendente dalla lingua. Solo laddove le strategie di layout degli oggetti di due implementazioni della lingua sono simili a , è possibile ottenere tale collegamento.

Poiché C e C++ sono lingue diverse, ciò significa che non è possibile fare affidamento su questa "funzione" dei compilatori comuni.

forte è nota 5 nella stessa sezione (sottolineatura mia):

Se due dichiarazioni dichiarano funzioni con lo stesso nome e parametro di tipo-list (8.3.5) di essere membri di lo stesso namespace o dichiarare oggetti con lo stesso nome a essere membri dello stesso namespace e le dichiarazioni danno i nomi collegamenti linguistici diversi, il programma è mal formato; non è richiesta la diagnostica se le dichiarazioni sono visualizzate in diverse unità di traduzione.

Quindi, direi che quello che hai fatto, non è garantito per funzionare secondo lo standard, e il compilatore non è tenuto a stampare una diagnostica per l'esempio che hai dato, perché le dichiarazioni sono in diverse unità di traduzione.

FYI, "funziona per me" con gcc e g ++ versione 4.2.1 su Snow Leopard.

+0

Citazione completamente corretta, ma si noti che ha anche un vantaggio: poiché è comunque definita dall'implementazione, se funziona funziona. – MSalters

+0

Sì, ma forse funziona perché funziona con quella particolare versione del compilatore? Ho provato a guardare la documentazione di gcc ma non ho trovato alcuna informazione. Per quanto ho capito, la domanda è più di una domanda "teorica", cercando di scoprire se lo standard consente tali dichiarazioni. Dubito che l'OP voglia fare una cosa del genere in codice reale, dato che è così facile evitare di farlo. –

+0

Bene, il codice è in produzione e funziona, ma mi piacerebbe mantenere gli standard del codice compatibili, in quanto nuove piattaforme o compilatori potrebbero essere presi di mira in futuro. – olovb

11

In molti casi, ma non tutti, un riferimento può essere implementato con un puntatore "auto-dereferenziato". Che qualsiasi particolare compilatore tratti una funzione con C linkage e un parametro di riferimento in questo modo non è garantito nello standard C++, e dovresti trattarlo come un dettaglio di implementazione.

Non è difficile scrivere una funzione di inoltro che prende un puntatore e chiama la funzione, se avete bisogno di fare questo senza fare affidamento su dettagli di implementazione:

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

Un riferimento è un nome alternativo per un oggetto. Tecnicamente, non c'è nulla in C che possa mappare direttamente a un riferimento C++.

L'ovvia implementazione di un riferimento è come un puntatore (costante) che viene sottoposto a dereferenziazione ogni volta che viene utilizzato. Quindi, a seconda di come il compilatore implementa riferimenti, il tuo codice potrebbe funzionare. Ma non è corretto.

La soluzione è scrivere una funzione C che riceve o un oggetto reale o un puntatore a tale oggetto e chiama la funzione C++ da questo.

+1

Non è garantito, ma non è necessariamente errato. Come le altre lingue chiamano le funzioni C++ (incluse le funzioni "extern" C "' C++) non rientra nell'ambito dello standard C++. E per quanto riguarda C, la funzione è stata dichiarata e chiamata correttamente. Questo è un problema di implementazione e se è possibile, dovrebbe o non deve dipendere da esso, varierà in base ai requisiti del progetto. –

0

Questo è quello che Bjarne Stroustrup ha da dire sui riferimenti:.

"La maggior parte dei riferimenti sono attrezzi sono realizzati tramite una variabile puntatore, che è un punto di riferimento di solito prende una parola di memoria Tuttavia, un riferimento che è usato puramente localmente può - e spesso lo è - eliminato dall'ottimizzatore ".

Ad esempio:

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]; 
    } 
} 

In questo caso, p non ha bisogno di essere immagazzinate nella memoria (forse esiste solo in un registro, forse scompare nelle istruzioni).

+0

Intendi 'int (& p) [100] = v [i] .b;'; anche s /, /;/e v.size(). –

+0

@Roger: Grazie, ho modificato il mio post :) –

+4

Sarebbe bello se tu avessi una citazione più completa. * Dove * ha fatto Stroustrup? (Sito Web, titolo del libro, edizione e numero di pagina) D'altra parte, * sappiamo già * i riferimenti sono spesso implementati come puntatori. Ecco perché il codice nella domanda funziona come fa oggi. Non vedo il punto che stavi cercando di fare con questa risposta, specialmente riguardo ai parametri delle funzioni con il collegamento C. –

1

Penso che tu abbia già delle buone risposte, quindi sottolineo qualcosa in più sulla tua domanda.

La mia comprensione umile è che l'esterno crea semplicemente un simbolo C.

Così il vostro esempio ancora "sembra funzionare" (gcc/g ++) anche quando aggiungo un oggetto di classe - ho dovuto cercare ir a crederci:

class A {}; 

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