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.
C riconosce anche una parola chiave 'inline'? Pensavo che lo scrivessero in modo diverso. –
@BenVoigt: Sì, ha una semantica leggermente diversa. – Deduplicator
Non so perché pensavo che la versione C avesse dei caratteri di sottolineatura. –