2016-02-06 26 views
24

Grałem z numerem labels as values i skończyłem z tym kodem.'goto * foo', gdzie foo nie jest wskaźnikiem. Co to jest?

int foo = 0; 
goto *foo; 

My C/C++ doświadczenie mówi mi *foo oznacza dereference foo i że nie będzie to skompilować ponieważ foo nie jest wskaźnikiem. Ale to się kompiluje. Co to właściwie robi?

gcc (Ubuntu 4.9.2-0ubuntu1~12.04) 4.9.2, jeśli to ważne.

+2

To znany błąd. Zobacz moją zaktualizowaną odpowiedź. –

Odpowiedz

23

To znany błąd w gcc.

gcc ma documented extension który umożliwia zestawienie postaci

goto *ptr; 

gdzie ptr może być dowolne wyrażenie typu void*. W ramach tego rozszerzenia podanie jednoargumentowej nazwy && na nazwę etykiety powoduje podanie adresu etykiety typu: void*.

W przykładzie:

int foo = 0; 
goto *foo; 

foo wyraźnie jest typu int, nie typu void*. Wartość int można przekonwertować na void*, ale tylko w przypadku jawnego rzutowania (z wyjątkiem specjalnego przypadku zerowej stałej wskaźnika, która nie ma tu zastosowania).

Samo wyrażenie *foo jest prawidłowo diagnozowane jako błąd. I tak:

goto *42; 

kompiluje bez błędów (wygenerowany kod maszyna wydaje się być skok zająć 42, jeśli mam poprawnie czyta kod montaż).

Szybkie eksperyment wskazuje, że gcc generuje ten sam kod montażowy

goto *42; 

jak dla

goto *(void*)42; 

Ten ostatni jest prawidłowe stosowanie udokumentowanego rozszerzenia, i to, co powinieneś prawdopodobnie, jeśli z jakiegoś powodu chcesz przeskoczyć pod adres 42.

Zgłosiłem bug report - który został szybko zamknięty jako duplikat this bug report, przesłane w 2007.

+0

czy to jest to samo, co skok do adresu 'foo'? –

+1

@ Ælex: Nie przeskakuje do adresu 'foo'; przeskakuje do adresu zawartego w 'foo'. –

+0

Dereferencja 'void *' nie jest masowo bardziej "poprawna" niż dereferencja int ... – Leushenko

6

Wydaje się być błędem GCC. Oto wynik klang jako porównanie. Wydaje się, że są to błędy, których powinniśmy się spodziewać.

$ 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 kod źródłowy:

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

To nie jest błąd, a konsekwencją GCC's Labels and Values extension. Zgaduję, że mieli na myśli takie szybkie tabele jak i JIT.Dzięki tej funkcji (MIS), można skoczyć funkcji

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

i nie cudownie gustowne rzeczy jak skok na sznurku dosłownym

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

Try it online!

nie zapomnij ta arytmetyczna wskazówka jest dozwolona!

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"); 

Zostawię dodatkowe konsekwencje jako ćwiczenie dla czytelnika (argv[0], __FILE__, __DATE__, etc.)

Zauważ, że musisz upewnić się, że masz uprawnienia wykonywalny dla obszaru pamięć, do której skaczesz.

+1

W rzeczywistości jest to błąd. Jest udokumentowane, aby zezwolić na 'goto * ptr;' gdzie 'ptr' jest typu' void * '. 'goto * foo;' gdzie 'foo' jest typu' int * 'narusza własna dokumentację rozszerzenia gcc. Zobacz [moja odpowiedź] (https://stackoverflow.com/a/35237062/827263) i [ten raport o błędzie] (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=32122). –

+0

@KeithThompson Widzę, gdzie dokumentacja określa, że ​​adres etykiety ma typ 'void *', ale gdzie określa, że ​​nie 'void *' argumenty 'goto *' są niedozwolone? – ceilingcat

+0

"Na przykład' goto * ptr; ' Dowolne wyrażenie typu' void * 'jest dozwolone." –

Powiązane problemy