2014-10-28 21 views
6

Questo codice genera un comportamento indefinito?Funzioni inline "C" esterne

header.h

#ifdef __cplusplus 
extern "C" 
{ 
#endif 

inline int foo(int a) 
{ 
    return a * 2; 
} 

#ifdef __cplusplus 
} 
#endif 

def.c

#include "header.h" 

extern inline int foo(int a); 

use.c

#include "header.h" 

int bar(int a) 
{ 
    return foo(a + 3); 
} 

main.cpp

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

extern "C" 
{ 
    int bar(int a); 
} 

int main(int argc, char** argv) 
{ 
    printf("%d\n", foo(argc)); 
    printf("%d\n", bar(argc)); 
} 

Questo è un esempio di un programma in cui una funzione inline deve essere utilizzata sia in C e C++. Funzionerebbe se def.c fosse rimosso e foo non fosse usato in C? (Ciò presuppone che il compilatore C è C99.)

Questo codice funziona quando viene compilato con:

gcc -std=c99 -pedantic -Wall -Wextra -c -o def.o def.c 
g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp 
gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c 
g++ -std=c++11 -pedantic -Wall -Wextra -o extern_C_inline def.o main.o use.o 

foo è solo in extern_C_inline una volta perché le diverse versioni che le uscite del compilatore in diversi file oggetto vengono fuse , ma vorrei sapere se questo comportamento è specificato dallo standard. Se rimuovo la definizione extern di foo e la facciamo static allora foo apparirà nello extern_C_inline più volte perché il compilatore la stampa in ogni unità di compilazione.

+0

C riconosce anche una parola chiave 'inline'? Pensavo che lo scrivessero in modo diverso. –

+5

@BenVoigt: Sì, ha una semantica leggermente diversa. – Deduplicator

+0

Non so perché pensavo che la versione C avesse dei caratteri di sottolineatura. –

risposta

5

Il programma è valido come scritto, ma è necessario il def.c per garantire che il codice funzioni sempre con tutti i compilatori e qualsiasi combinazione di livelli di ottimizzazione per i diversi file.

Perché c'è una dichiarazione con extern su di esso, def.c fornisce una definizione esterna della funzione foo(), che è possibile confermare con nm:

$ nm def.o 
0000000000000000 T foo 

Questa definizione sarà sempre presente in def.o non importa quanto che il file è compilato.

In use.c v'è una definizione linea di foo(), ma secondo 6.7.4 nello standard C non è specificato se la chiamata a foo() utilizza tale definizione in linea o utilizza una definizione esterna (in pratica se utilizza la la definizione in linea dipende dal fatto che il file sia ottimizzato o meno). Se il compilatore sceglie di utilizzare la definizione in linea, funzionerà. Se sceglie di non utilizzare la definizione in linea (ad esempio perché è compilata senza ottimizzazioni), allora è necessaria una definizione esterna in qualche altro file.

senza ottimizzazione use.o ha un riferimento indefinito:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c 
$ nm use.o 
0000000000000000 T bar 
       U foo 

Ma con ottimizzazione non è così:

$ gcc -std=c99 -pedantic -Wall -Wextra -c -o use.o use.c -O3 
$ nm use.o 
0000000000000000 T bar 

In main.cpp ci sarà una definizione di foo() ma sarà tipicamente generare un debole simbolo, quindi potrebbe non essere mantenuto dal linker se un'altra definizione viene trovata in un altro oggetto. Se il simbolo debole esiste, può soddisfare qualsiasi riferimento possibile in use.o che richiede una definizione esterna, ma se il compilatore è in linea foo() in main.o, allora potrebbe non emettere alcuna definizione di foo() in main.o, e quindi la definizione in def.o sarebbe ancora necessaria per soddisfare use.o

senza ottimizzazione main.o contiene un simbolo debole:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp 
$ nm main.o 
       U bar 
0000000000000000 W foo 
0000000000000000 T main 
       U printf 

Tuttavia compilazione main.cpp con -O3 inline la chiamata a foo e tH e compilatore non emette alcun simbolo per esso:

$ g++ -std=c++11 -pedantic -Wall -Wextra -c -o main.o main.cpp -O3 
$ nm main.o 
       U bar 
0000000000000000 T main 
       U printf 

Quindi, se foo() è non inline in use.o ma è inline in main.o quindi è necessario la definizione esterna in def.o

sarebbe lavoro se def.c è stato rimosso e foo non è stato utilizzato in C?

Sì. Se foo viene utilizzato solo nel file C++, non è necessaria la definizione esterna di foo in def.o perché main.o contiene la propria definizione (debole) o in linea la funzione. La definizione in foo.o è necessaria solo per soddisfare le chiamate non inline a foo da un altro codice C.


parte: il compilatore C++ può omettere la generazione di qualsiasi simbolo per foo quando l'ottimizzazione main.o perché standard ++ C dice che una funzione dichiarata inline in un'unità di traduzione tutte unità di traduzione deve essere dichiarato ancorato in , e per chiamare una funzione dichiarata inline la definizione deve essere disponibile nello stesso file della chiamata. Ciò significa che il compilatore sa che se un altro file vuole chiamare foo() allora quell'altro file deve contenere contenere la definizione di foo(), quindi quando l'altro file viene compilato il compilatore sarà in grado di generare un'altra definizione di simbolo debole della funzione (o in linea) come necessario. Quindi non è necessario produrre foo in main.o se tutte le chiamate in main.o sono state inline.

Questi sono semantica differnet da C, dove la definizione ancorato in use.c potrebbe essere ignorato dal compilatore, e la definizione esterna in def.o deve esistere anche se nulla in def.c chiama.

+0

Perché non è sufficiente rispondere che _ "non è coinvolto alcun compilatore C in questo" _ poiché il punto è _C linkage_, non _C compiler_, e anche il codice scritto in un 'extern" C "' viene compilato da un compilatore C++ ? – user2485710

+1

@ user2485710, perché non è vero. 'def.c' e' use.c' sono compilati da un compilatore C. –

+0

@JonathanSecondo con certezza, ma in una domanda che inizia con "extern" C "', che un C++ costruisce solo, quante volte questa cosa verrà compilata con un semplice compilatore 'C'? Penso che la domanda sia influenzata dal fatto che OP sta assumendo che anche quando usa quel codice in un codice C++ viene compilato da un compilatore C. – user2485710

Problemi correlati