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ì)?

È stato utile?

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top