2016-02-06 18 views
24

Stavo giocando con labels as values e ho finito con questo codice.'goto * foo' dove foo non è un puntatore. Cos'è questo?

int foo = 0; 
goto *foo; 

mio C/C++ esperienza mi dice *foo significa dereference foo e che questo non sarà compilato perché foo non è un puntatore. Ma lo fa compilare. Cosa fa in realtà questo?

gcc (Ubuntu 4.9.2-0ubuntu1~12.04) 4.9.2, se importante.

+2

È un bug noto. Vedi la mia risposta aggiornata. –

risposta

23

Questo è un bug noto in gcc.

gcc ha una documented extension che permette una dichiarazione della forma

goto *ptr; 

dove ptr può essere qualsiasi espressione di tipo void*. Come parte di questa estensione, l'applicazione di un unario && a un nome di etichetta produce l'indirizzo dell'etichetta, di tipo void*.

Nel tuo esempio:

int foo = 0; 
goto *foo; 

foo è chiaramente di tipo int, non di tipo void*. Un valore int può essere convertito in void*, ma solo con un cast esplicito (tranne nel caso speciale di una costante puntatore nullo, che non si applica qui).

L'espressione *foo di per sé è correttamente diagnosticata come errore. E questo:

goto *42; 

compila senza errori (il codice macchina generato sembra essere un salto per affrontare 42, se sto leggendo il codice assembly correttamente).

Un esperimento rapido indica che gcc genera lo stesso codice assembly per

goto *42; 

come fa per

goto *(void*)42; 

Quest'ultimo è un corretto utilizzo dell'estensione documentata, ed è ciò che si dovrebbe probabilmente se, per qualche ragione, vuoi saltare all'indirizzo 42.

Ho inviato un bug report - che è stato rapidamente chiuso come duplicato di this bug report, presentato nel 2007.

+0

equivale a saltare all'indirizzo 'pippo'? –

+1

@ Ælex: non salta all'indirizzo di "pippo"; salta all'indirizzo contenuto in 'pippo'. –

+0

Il dereferenziamento di un 'void *' non è molto più "corretto" del dereferenziamento di un int ... – Leushenko

6

Sembra un bug GCC. Ecco un risultato clang come confronto. Sembra che questi siano gli errori che dovremmo aspettarci.

$ cc -v 
Apple LLVM version 7.0.2 (clang-700.1.81) 
Target: x86_64-apple-darwin15.3.0 
Thread model: posix 
$ cc goto.c 
goto.c:5:7: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const void *' [-Wint-conversion] 
     goto *foo; 
      ^~~~ 
goto.c:5:2: error: indirect goto in function with no address-of-label expressions 
     goto *foo; 
     ^
1 warning and 1 error generated. 

goto.c codice sorgente:

int main(int argc, char const *argv[]) 
{ 
    int foo = 0; 
    goto *foo; 
} 
0

Questo non è un bug, piuttosto una conseguenza di GCC's Labels and Values extension. Immagino che abbiano in mente cose come i tavoli di salto veloce e JIT.Con questa (dis) funzione, è possibile salto nel funzioni

// c 
goto *(int *)exit; 
// c++ 
goto *reinterpret_cast<int *>(std::exit); 

e fare le cose meravigliosamente buon gusto come il salto in una stringa letterale

goto *&"\xe8\r\0\0\0Hello, World!Yj\1[j\rZj\4X\xcd\x80,\f\xcd\x80"; 

Try it online!

Non dimenticate quell'aritmetica del puntatore è permessa!

goto *(24*(a==1)+"\xe8\7\0\0\0Hello, Yj\1[j\7Zj\4X\xcd\x80\xe8\6\0\0\0World!Yj\1[j\6Zj\4X\xcd\x80,\5\xcd\x80"); 

lascerò conseguenze aggiuntive come esercizio al lettore (argv[0], __FILE__, __DATE__, etc.)

Si noti che è necessario assicurarsi di avere il permesso eseguibile per l'area di memoria a cui si salta.

+1

In effetti è un bug. È documentato per consentire 'goto * ptr;' dove 'ptr' è di tipo' void * '. 'goto * foo;' dove 'foo' è di tipo 'int *' viola la documentazione di gcc sull'estensione. Vedi [la mia risposta] (https://stackoverflow.com/a/35237062/827263) e [questa segnalazione di bug] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=32122). –

+0

@KeithThompson Vedo dove la documentazione specifica che l'indirizzo di un'etichetta ha tipo "void *" ma dove specifica che gli argomenti non void * 'di goto *' non sono consentiti? – ceilingcat

+0

"Ad esempio,' goto * ptr; ' Qualsiasi espressione di tipo" void * "è consentita." –

Problemi correlati