Differenza nel legame tra C e C ++?
-
19-09-2019 - |
Domanda
Ho letto le domande esistenti sul collegamento esterno / interno qui su SO. La mia domanda è diverso - cosa succede se ho più definizioni della stessa variabile con collegamento esterno in diverse unità di traduzione in C
e C++
Ad esempio:
/*file1.c*/
typedef struct foo {
int a;
int b;
int c;
} foo;
foo xyz;
/*file2.c*/
typedef struct abc {
double x;
} foo;
foo xyz;
Utilizzando Dev-C ++ e come un programma C, il programma di cui sopra compila e collegamenti perfettamente; che dà un errore ridefinizione multiplo se lo stesso è compilato come programma C ++. Perché dovrebbe funzionare in C e che cosa è la differenza con C ++? E 'questo un comportamento indefinito e compilatore-dipendente? Come "cattivo" è questo codice e cosa devo fare se voglio refactoring (che ho incontrato un sacco di vecchio codice scritto così)?
Soluzione
Sia C e C ++ hanno una "una regola definizione" che è che ciascun oggetto può essere definito solo una volta in qualsiasi programma. Le violazioni di questa regola causa comportamento non definito che significa che si può o non può visualizzare un messaggio di diagnostica durante la compilazione.
C'è una differenza linguistica tra le seguenti dichiarazioni nell'ambito di file, ma non riguarda direttamente il problema con il vostro esempio.
int a;
In C questa è una definizione provvisoria. Può essere amalgamato con altre definizioni tentativi nella stessa unità di conversione per formare un'unica definizione. In C ++ è sempre una definizione (è necessario utilizzare extern
dichiarare un oggetto senza definire) e eventuali definizioni successive dello stesso oggetto nella stessa unità di conversione sono un errore.
Nel tuo esempio entrambe le unità di traduzione hanno una definizione (in conflitto) di xyz
dalle loro definizioni timidi.
Altri suggerimenti
Questo è causato dal nome storpiatura C ++ 's. Da Wikipedia :
La prima compilatori C ++ sono stati implementato come traduttori a sorgente C codice, che sarebbe poi compilato da un compilatore C al codice oggetto; perché di questo, i nomi dei simboli dovevano conformarsi le regole identificatori C. Anche più tardi, con l'emergere di compilatori che codice macchina prodotta o montaggio direttamente, linker del sistema in genere non ha sostenuto i simboli C ++, e mangling era ancora necessario.
Per quanto riguarda compatibilità :
Al fine di dare fornitori del compilatore standard maggiore libertà, il C ++ commissione ha deciso di non dettare il implementazione di nome storpiatura, gestione delle eccezioni, e altri funzionalità specifiche di attuazione. Il aspetto negativo di questa decisione è che codice oggetto prodotto dalla differente compilatori si pensa che sia incompatibile. Vi sono, tuttavia, norme di terze parti per la particolare macchine o sistemi operativi che tentativo di standardizzare su compilatori tali piattaforme (ad esempio C ++ ABI [18]); alcuni compilatori adottano un standard secondario per questi articoli.
Da http://www.cs.indiana.edu/~welu/notes/ node36.html il seguente esempio è dato:
Ad esempio per il codice C seguente
int foo(double*);
double bar(int, double*);
int foo (double* d)
{
return 1;
}
double bar (int i, double* d)
{
return 0.9;
}
La sua tabella dei simboli sarebbe (da dump -t
)
[4] 0x18 44 2 1 0 0x2 bar
[5] 0x0 24 2 1 0 0x2 foo
Per stesso file, se compilare in g ++, quindi la tabella dei simboli sarebbe
[4] 0x0 24 2 1 0 0x2 _Z3fooPd
[5] 0x18 44 2 1 0 0x2 _Z3bariPd
_Z3bariPd
: una funzione che si chiama bar e la cui prima arg è intero e secondo parametro è puntatore di raddoppiare.
C ++ non consente un simbolo da definire più di una volta. Non sono sicuro che il linker C sta facendo, una buona congettura potrebbe essere che le mappe semplicemente entrambe le definizioni sullo stesso simbolo, che naturalmente causare gravi errori.
Per il porting Vorrei provare a mettere i contenuti delle singole C-file in spazi dei nomi anonimi, il che rende essenzialmente i simboli diversi, e locale per il file, in modo che non si scontrano con lo stesso nome altrove.
Il programma C permette questo e tratta la memoria un po 'come un sindacato. Verrà eseguito, ma non si può dare quello che vi aspettavate.
Il programma C ++ (che è più forte tipizzato) rileva correttamente il problema e vi chiede di risolvere il problema. Se si vuole veramente un sindacato, la dichiareremo come uno. Se si desidera che due oggetti distinti, limitarne la portata.
Hai trovato il Regola Uno Definizione . Chiaramente il programma ha un bug, in quanto
- Non ci può essere solo un oggetto di nome
foo
una volta che il programma è collegato. - Se alcuni file di origine include tutti i file di intestazione, vedrà due definizioni di
foo
.
compilatori C ++ possono aggirare # 1 a causa di "nome storpiatura": il nome della variabile nel programma collegato può essere diversa da quella che avete scelto. In questo caso, non è necessario, ma è probabilmente come il compilatore ha rilevato il problema. # 2, però, rimane, quindi non si può fare.
Se davvero si vuole sconfiggere il meccanismo di sicurezza, è possibile disattivare pressare in questo modo:
extern "C" struct abc foo;
... altro file ...
extern "C" struct foo foo;
extern "C"
indica al linker di utilizzare convenzioni C ABI.