Saya telah menjalankan kode berikut melalui berbagai kompiler:
int main()
{
float **a;
void **b;
b = a;
}
Dari apa yang saya sudah mampu mengumpulkan, void **
adalah tidak pointer generik yang berarti bahwa setiap konversi dari pointer lain tidak harus mengkompilasi atau setidaknya melempar peringatan. Namun, inilah hasil saya (semua dilakukan pada Windows):
- gcc - Melempar peringatan, seperti yang diharapkan.
- g ++ - Melempar kesalahan, seperti yang diharapkan (ini disebabkan oleh pengetikan C ++ yang kurang permisif, kan?)
- MSVC (cl.exe) - Melemparkan tidak ada peringatan sama sekali, bahkan dengan / Wall ditentukan.
Pertanyaan saya adalah: Apakah saya kehilangan sesuatu tentang semuanya dan apakah ada alasan khusus mengapa MSVC tidak menghasilkan peringatan? MSVC memang menghasilkan peringatan saat mengkonversi dari void **
menjadi float **
.
Catatan lain: Jika saya mengganti a = b
dengan konversi eksplisit a = (void **)b
, tidak ada kompiler yang memberikan peringatan. Saya pikir ini harus menjadi pemain yang tidak valid, jadi mengapa tidak ada peringatan?
Alasan saya mengajukan pertanyaan ini adalah karena saya mulai mempelajari CUDA dan dalam Panduan Pemrograman resmi ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) kode berikut dapat ditemukan:
// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);
yang harus melakukan konversi implisit ke void **
untuk &d_A
, karena argumen pertama cudaMalloc
adalah tipe void **
. Kode serupa dapat ditemukan di seluruh dokumentasi. Apakah ini hanya pekerjaan ceroboh di ujung NVIDIA atau apakah saya, sekali lagi, melewatkan sesuatu? Karena nvcc
menggunakan MSVC, kode ini dikompilasi tanpa peringatan.
void**
bukan penunjuk generik. Hanyavoid*
itu.(void**)
adalah pemeran gaya c eksplisit. Ia memberi tahu kompiler untuk tidak melihat dengan cermat apa yang Anda lakukan dan memercayai Anda. Ini merupakan override eksplisit dari sistem keselamatan jenis dan kompiler diharuskan untuk menerima konversi apa pun pada dasarnya. Pemain gaya C harus dihindari, mereka terlalu kuat. Gunakan C ++ gips sepertistatic_cast
yang akan mengeluh jika Anda mencoba melakukan sesuatu yang tidak masuk akal.Jawaban:
Tugas ini tanpa gips merupakan pelanggaran kendala, jadi kompiler yang memenuhi standar akan mencetak peringatan atau kesalahan. Namun, MSVC tidak sepenuhnya memenuhi implementasi C.
Konversi pointer melalui gips diperbolehkan dalam beberapa situasi. Standar C mengatakan yang berikut di bagian 6.3.2.3p7:
Jadi, Anda dapat mengkonversi antara jenis pointer asalkan tidak ada masalah pelurusan dan Anda hanya mengkonversi kembali (kecuali targetnya adalah a
char *
).Agaknya, fungsi ini mendereferensi pointer yang diberikan dan menulis alamat beberapa memori yang dialokasikan. Itu berarti sedang mencoba untuk menulis ke
float *
seolah-olah itu avoid *
. Ini tidak sama dengan konversi khas ke / dari avoid *
. Sebenarnya ini terlihat seperti perilaku yang tidak terdefinisi, meskipun "berhasil" karena prosesor x86 modern (saat tidak dalam mode nyata) menggunakan representasi yang sama untuk semua jenis pointer.sumber
&d_A
tidak memiliki jenis yang diperlukan?