Konversi untuk membatalkan ** pada kompiler yang berbeda

9

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 = bdengan 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 cudaMallocadalah 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 nvccmenggunakan MSVC, kode ini dikompilasi tanpa peringatan.

CaptainProton42
sumber
3
Kesalahan dari 3 yang diposting langsung: godbolt.org/z/GQWMNo
Richard Critten
3
Kesalahan kode untuk saya dengan MSVC. Versi mana yang Anda gunakan? Tapi ya, void**bukan penunjuk generik. Hanya void*itu.
NathanOliver
Terima kasih atas jawaban cepatnya! 19.24.28315 untuk x64 rupanya? Saya belum pernah menggunakan MSVC sebelumnya.
CaptainProton42
2
(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 seperti static_castyang akan mengeluh jika Anda mencoba melakukan sesuatu yang tidak masuk akal.
François Andrieux
@RichardCritten salah bahasa - tidak ada kesalahan godbolt.org/z/RmFpgN C ++ cuda membutuhkan gips eksplisit seperti biasanya.
P__J__

Jawaban:

4

Apakah saya kehilangan sesuatu tentang semuanya dan apakah ada alasan khusus mengapa MSVC tidak menghasilkan peringatan? MSVC memang menghasilkan peringatan saat mengkonversi dari void ** ke float **

Tugas ini tanpa gips merupakan pelanggaran kendala, jadi kompiler yang memenuhi standar akan mencetak peringatan atau kesalahan. Namun, MSVC tidak sepenuhnya memenuhi implementasi C.

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?

Konversi pointer melalui gips diperbolehkan dalam beberapa situasi. Standar C mengatakan yang berikut di bagian 6.3.2.3p7:

Pointer ke tipe objek dapat dikonversi ke pointer ke tipe objek yang berbeda. Jika pointer yang dihasilkan tidak selaras dengan benar untuk tipe yang direferensikan, perilaku tidak terdefinisi. Kalau tidak, ketika dikonversi kembali lagi, hasilnya harus membandingkan sama dengan pointer asli. Ketika pointer ke objek dikonversi ke pointer ke tipe karakter, hasilnya menunjuk ke byte terendah dari objek. Peningkatan hasil berturut-turut, hingga ukuran objek, menghasilkan pointer ke byte tersisa dari objek.

Jadi, Anda dapat mengkonversi antara jenis pointer asalkan tidak ada masalah pelurusan dan Anda hanya mengkonversi kembali (kecuali targetnya adalah a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Apakah ini hanya pekerjaan ceroboh di ujung NVIDIA atau apakah saya, sekali lagi, melewatkan sesuatu?

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 a void *. Ini tidak sama dengan konversi khas ke / dari a void *. 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.

dbush
sumber
@ debush Sangat informatif, terima kasih! Saya mengerti mengapa itu berhasil jika berhasil. Namun, tidakkah kebanyakan penyusun membuat peringatan atau bahkan kesalahan karena &d_Atidak memiliki jenis yang diperlukan?
CaptainProton42
3
Hati-hati bagaimana Anda menafsirkan ini, mungkin ada dan ada trik template antara jika Anda mengkompilasi CUDA dengan kompiler C ++
talonmies