2015-04-25 16 views
6

In C++, è possibile che i valori del puntatore siano costanti in fase di compilazione. Questo è vero, altrimenti, i parametri del modello non di tipo e constexpr non funzioneranno con i puntatori. Tuttavia, per quanto ne so, gli indirizzi di funzioni e oggetti di archiviazione statica sono noti (almeno) in fase di collegamento anziché in fase di compilazione. Di seguito è riportato un esempio:Confusione relativa ai valori del puntatore durante la verifica della fase di compilazione

main.cpp

#include <iostream> 

template <int* p> 
void f() { std::cout << p << '\n'; } 

extern int a; 

int main() { 
    f<&a>(); 
} 

a.cpp

Sto solo chiedendo come potrebbe essere conosciuto l'indirizzo del a durante la compilazione main.cpp . Spero che qualcuno possa spiegarlo un po 'a me.

In particolare, in considerazione questa dovrebbe l'archiviazione per arr da assegnare

template <int* p, int* pp> 
constexpr std::size_t f() { 
    return (p + 1) == (pp + 7) ? 5 : 10; 
} 

int main() { 
    int arr[f<&a, &b>()] = {}; 
} 

Come?

PLUS: Questo meccanismo sembra essere piuttosto robusto. Anche quando ho abilitato Randomized Base Address, si ottiene l'output corretto.

+3

In g ++, il nome della variabile il cui indirizzo è preso è storpiato in nome della funzione: http://coliru.stacked-crooked.com/a/ee352366c870c010 – dyp

+0

@dyp che ha senso! – Lingxi

+0

@dyp Il linker alla fine aggiusterà il valore dell'indirizzo 'p' nel codice binario dell'istanza del modello. Posso capirlo in questo modo? – Lingxi

risposta

4

il compilatore non ha bisogno di conoscere il valore di di &a in fase di compilazione più del necessario degli indirizzi di funzione.

Pensate in questo modo: il compilatore istanzia il vostro modello di funzione con &a come parametro e genera "codice oggetto" (in qualsiasi formato utilizzato per passare al linker). Il codice oggetto sarà simile (beh non lo farà, ma si ottiene l'idea):

func f__<funky_mangled_name_to_say_this_is_f_for_&a>__: 
    reg0 <- /* linker, pls put &std::cout here */ 
    reg1 <- /* hey linker, stuff &a in there ok? */ 
    call std::basic_stream::operator<<(int*) /* linker, fun addr please? */ 
    [...] 

Se si crea un'istanza di f<b&>, assumendo b è un'altra statica, compilatore globale non la stessa cosa:

func f__<funky_mangled_name_to_say_this_is_f_for_&b>__: 
    reg0 <- /* linker, pls put &std::cout here */ 
    reg1 <- /* hey linker, stuff &b in there ok? */ 
    call std::basic_stream::operator<<(int*) /* linker, fun addr please? */ 
    [...] 

E quando il codice chiama per chiamare uno di questi:

fun foo: 
    call f__<funky_mangled_name_to_say_this_is_f_for_&a>__ 
    call f__<funky_mangled_name_to_say_this_is_f_for_&b>__ 

Quali esatta funzione da chiamare è codificata nel nome della funzione alterati. Il codice generato non dipende dal valore di runtime di &a o &b. Il compilatore sa che ci saranno cose simili in fase di esecuzione (l'hai detto tu), questo è tutto ciò di cui ha bisogno. Permetterà al linker di riempire gli spazi vuoti (o di urlarti se non rispetti la tua promessa).


Per la vostra Inoltre ho paura io non sono abbastanza familiarità sulle regole constexpr, ma i due compilatori che ho mi dicono che questa funzione sarà valutata in fase di esecuzione, che, secondo loro, rende il codice non conforme. (Se sono sbagliato, allora la risposta di cui sopra è, almeno, incompleto.)

template <int* p, int* pp> 
constexpr std::size_t f() { 
    return (p + 1) == (pp + 7) ? 5 : 10; 
} 

int main() { 
    int arr[f<&a, &b>()] = {}; 
} 

clang 3,5 a standard C++ 14 conforme modalità:

$ clang++ -std=c++14 -stdlib=libc++ t.cpp -pedantic 
t.cpp:10:10: warning: variable length arrays are a C99 feature [-Wvla-extension] 
    int arr[f<&a, &b>()]; 
     ^
1 warning generated. 

GCC g ++ 5.1, stessa modalità :

$ g++ -std=c++14 t.cpp -O3 -pedantic 
t.cpp: In function 'int main()': 
t.cpp:10:22: warning: ISO C++ forbids variable length array 'arr' [-Wvla] 
    int arr[f<&a, &b>()]; 
+0

Che dire di questo 'template constexpr std :: size_t f() {return (p + 1) == (pp + 7)? 5: 10; } ', e io uso il valore restituito come limite in una definizione di matrice? – Lingxi

+0

Quindi come allocare la memoria per l'array 'int arr [f <&a, &b>()] = {};'? – Lingxi

+0

Interessante domanda. Non riesco a farlo compilare in modalità C++ 11 (clang ++ e g ++ mi dicono che è un VLA). Lo stesso con la modalità C++ 14 (g ++ 5.1/clang ++ 3.5) – Mat

1

Per quanto ne so, le variabili della memoria statica e delle funzioni vengono memorizzate semplicemente come simboli/segnaposto nella tabella dei simboli durante la compilazione. È nella fase di collegamento quando i segnaposto vengono risolti.

Il compilatore emette il codice macchina mantenendo intatti i segnaposto. Quindi il linker sostituisce i segnaposto delle variabili/funzioni con le rispettive posizioni di memoria. Quindi, anche in questo caso, compilando main.cpp senza compilare a.cpp e collegandolo, si è costretti ad affrontare l'errore del linker, come si può vedere qui http://codepad.org/QTdJCgle (Ho compilato solo main.cpp)