Arti dari int (*) (int *) = 5 (atau nilai integer apapun)

88

Saya tidak tahu ini:

int main() {
    int (*) (int *) = 5;
    return 0;
}

Tugas di atas dikompilasi dengan g ++ c ++ 11. Saya tahu itu int (*) (int *)adalah penunjuk ke fungsi yang menerima (int *)argumen sebagai dan mengembalikan int, tapi saya tidak mengerti bagaimana Anda bisa menyamakannya dengan 5. Awalnya saya pikir itu adalah fungsi yang secara konstan mengembalikan 5 (dari pembelajaran saya baru-baru ini di F #, mungkin, haha), lalu saya berpikir, secara singkat, bahwa penunjuk fungsi menunjuk ke lokasi memori 5, tetapi itu tidak berfungsi, jelas, dan begitu juga dengan nilai hex.

Berpikir bahwa itu bisa jadi karena fungsinya mengembalikan int, dan menugaskan int itu ok (entah bagaimana), saya juga mencoba ini:

int * (*) (int *) = my_ptr

where my_ptris type int *, type yang sama dengan function pointer kedua ini, seperti pada kasus pertama dengan type int. Ini tidak dapat dikompilasi. Menetapkan 5, atau nilai int apa pun, bukan my_ptr, tidak dikompilasi untuk penunjuk fungsi ini juga.

Jadi apa maksud dari tugas itu?

Perbarui 1

Kami mendapat konfirmasi bahwa ini adalah bug, seperti yang ditunjukkan pada jawaban terbaik. Namun, masih belum diketahui apa yang sebenarnya terjadi pada nilai yang Anda tetapkan ke penunjuk fungsi, atau apa yang terjadi dengan penetapan tersebut. Penjelasan (bagus) apa pun tentang itu akan sangat dihargai! Lihat hasil edit di bawah untuk kejelasan lebih lanjut tentang masalah tersebut.

Edit 1

Saya menggunakan gcc versi 4.8.2 (di Ubuntu 4.8.2)

Edit 2

Sebenarnya, menyamakannya dengan apa pun berfungsi pada kompiler saya. Bahkan menyamakannya dengan variabel std :: string, atau nama fungsi yang mengembalikan double, berhasil.

Edit 2.1

Menariknya, menjadikannya penunjuk fungsi ke fungsi apa pun yang mengembalikan tipe data yang bukan penunjuk, akan membiarkannya dikompilasi, seperti

std::string (*) () = 5.6;

Tapi begitu penunjuk fungsi adalah ke fungsi yang mengembalikan beberapa penunjuk, ia tidak dapat dikompilasi, seperti dengan

some_data_type ** (*) () = any_value;
Konrad
sumber
3
Hmm ... sepertinya tidak benar, dan dentang tidak menerimanya. Bisa jadi ekstensi gcc (atau bug).
Wintermute
4
g ++ dikompilasi, tetapi gcc tidak berfungsi:error: expected identifier or '(' before ')' token
tivn
3
@ 0x499602D Perhatikan bahwa kode tidak memberikan nama untuk penunjuk. Dengan int *x = 5Anda menamakannya x. Dengan int * (*x) (int *) = 5itu tidak akan dikompilasi. (meskipun itu akan dikompilasi sebagai kode C).
no
5
Kasus uji yang dikurangi: int(*) = 5;danint(*);
Johannes Schaub - litb

Jawaban:

60

Ini bug di g ++.

 int (*) (int *) 

adalah nama tipe.

Di C ++ Anda tidak dapat memiliki deklarasi dengan nama tipe tanpa pengenal.

Jadi ini dikompilasi dengan g ++.

 int (*) (int *) = 5;

dan ini mengkompilasi juga:

 int (*) (int *);

tetapi keduanya adalah deklarasi yang tidak valid.

EDIT :

TC menyebutkan di komentar bugzilla bug 60680 dengan kasus uji serupa tetapi belum disetujui . Bug dikonfirmasi di bugzilla.

EDIT2 :

Ketika dua deklarasi di atas berada pada lingkup file g ++ dengan benar mengeluarkan diagnostik (gagal mengeluarkan diagnostik pada lingkup blok).

EDIT3 :

Saya telah memeriksanya dan saya dapat mereproduksi masalah tersebut pada rilis terbaru g ++ versi 4 (4.9.2), versi pra-rilis terbaru 5 (5.0.1 20150412) dan versi eksperimental terbaru 6 (6.0.0 20150412).

ouah
sumber
5
MSVC menolak kode yang telah diedit denganerror C2059: syntax error : ')'
Weather Vane
Jika itu adalah nama tipe, mengapa tidak 'int (*) (int *) int_func;' kerja?
Konrad
1
Untuk bugzilla GCC, "BARU" adalah bug yang telah dikonfirmasi. (Bug yang belum dikonfirmasi "TIDAK DIKONFIRMASI").
TC
4
@KonradKapp: itu berfungsi dengan baik jika Anda mengatakan int (*int_func)(int *); yang menyatakan penunjuk fungsi bernama int_func.
Edward
3
@KonradK C ++ menggunakan notasi infix untuk menempatkan pengenal; alasan yang sama int x[5];dan tidakint[5] x;
MM
28

Ini bukan C ++ yang valid. Ingatlah bahwa karena kompilator khusus Anda kebetulan mengkompilasi, itu tidak membuatnya valid. Kompiler, seperti semua perangkat lunak yang kompleks, terkadang memiliki bug dan ini tampaknya salah satunya.

Sebaliknya, clang++keluhan:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Ini adalah perilaku yang diharapkan karena baris yang melanggar bukan C ++ yang valid. Ini dimaksudkan sebagai tugas (karena =) tetapi tidak berisi pengenal.

Edward
sumber
9

Seperti jawaban lain telah menunjukkan, itu adalah bug itu

int (*) (int *) = 5;

mengkompilasi. Perkiraan yang masuk akal dari pernyataan ini yang diharapkan memiliki makna adalah:

int (*proc)(int*) = (int (*)(int*))(5);

Sekarang procadalah pointer-to-function yang mengharapkan alamat 5menjadi alamat dasar dari fungsi yang mengambil int*dan mengembalikan int.

Pada beberapa mikrokontroler / mikroprosesor 5mungkin terdapat alamat kode yang valid, dan dimungkinkan untuk menemukan fungsi seperti itu di sana.

Pada kebanyakan komputer tujuan umum, halaman pertama memori (alamat 0-1023untuk halaman 4K) sengaja tidak valid (tidak dipetakan) untuk menangkap nullakses penunjuk.

Jadi, sementara perilaku bergantung pada platform, seseorang dapat mengharapkan kesalahan halaman terjadi saat *procdipanggil (misalnya, (*proc)(&v)). Sebelum waktu *procpemanggilan, tidak ada yang tidak biasa yang terjadi.

Kecuali jika Anda menulis penaut dinamis, Anda hampir pasti tidak harus menghitung alamat secara numerik dan menetapkannya ke variabel penunjuk-ke-fungsi.

Atsby
sumber
2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Baris perintah ini menghasilkan banyak file perantara. Yang pertama so.cpp.170r.expand,, mengatakan:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Ini masih belum menjawab apa yang terjadi dengan tepat, tetapi ini harus menjadi langkah ke arah yang benar.

Roland Illig
sumber
Menarik. Apa tujuan dari file perantara ini?
Konrad
@KonradKapp Untuk menghasilkan kode mesin dari kode manusia adalah proses yang cukup kompleks (terutama jika Anda ingin kompilator Anda mengoptimalkan keluarannya). Karena kompilasi sangat kompleks, tidak dilakukan dalam satu langkah, sebagian besar kompiler memiliki beberapa bentuk Representasi Menengah (IR).
11684
2
Alasan lain untuk memiliki IR adalah jika Anda memiliki IR yang terdefinisi dengan baik, Anda dapat memisahkan front-end dan back-end kompiler Anda. (Misalnya, front-end mengkompilasi C ke IR Anda, back-end mengkompilasi IR ke kode mesin Intel. Sekarang jika Anda ingin menambahkan dukungan ARM, Anda hanya memerlukan back-end kedua. Dan jika Anda ingin mengkompilasi Go, Anda hanya perlu front-end kedua, dan yang terpenting kompiler Go segera mendukung Intel dan ARM karena Anda dapat menggunakan kembali kedua back-end.
11684
@ 11684 Oke, masuk akal. Sangat menarik. Saya tidak dapat menentukan bahasa apa yang Roland berikan dalam jawaban ini ... sepertinya semacam perakitan dicampur dengan C.
Konrad
IR tidak perlu dapat dicetak; Saya tidak tahu apa yang digunakan gcc, ini mungkin hanya representasi yang dapat dicetak @KonradKapp
11684