Dalam bahasa yang sangat diketik seperti Java dan C #, void
(atau Void
) sebagai tipe kembali untuk metode tampaknya berarti:
Metode ini tidak mengembalikan apa pun. Tidak ada. Tidak kembali Anda tidak akan menerima apa pun dari metode ini.
Apa yang benar-benar aneh adalah bahwa dalam C, void
sebagai tipe pengembalian atau bahkan sebagai tipe parameter metode berarti:
Bisa jadi apa saja. Anda harus membaca kode sumber untuk mengetahuinya. Semoga berhasil. Jika itu sebuah pointer, Anda harus benar-benar tahu apa yang Anda lakukan.
Perhatikan contoh-contoh berikut dalam C:
void describe(void *thing)
{
Object *obj = thing;
printf("%s.\n", obj->description);
}
void *move(void *location, Direction direction)
{
void *next = NULL;
// logic!
return next;
}
Jelas, metode kedua mengembalikan pointer, yang menurut definisi bisa apa saja.
Karena C lebih tua dari Java dan C #, mengapa bahasa-bahasa ini mengadopsi yang void
berarti "tidak ada" sementara C menggunakannya sebagai "tidak ada atau apa pun (ketika pointer)"?
void
sementara contoh kode menggunakanvoid*
sesuatu yang sama sekali berbeda.Object
dalam satu kasus untuk disatukan.dynamic
jenis yang jarang digunakan?Jawaban:
Kata kunci
void
(bukan penunjuk) berarti "tidak ada" dalam bahasa tersebut. Ini konsisten.Seperti yang Anda catat,
void*
berarti "penunjuk ke apa saja" dalam bahasa yang mendukung pointer mentah (C dan C ++). Ini adalah keputusan yang disayangkan karena seperti yang Anda sebutkan, itu membuatvoid
dua hal berbeda.Saya belum dapat menemukan alasan historis di balik penggunaan kembali yang
void
berarti "tidak ada" dan "apa pun" dalam konteks yang berbeda, namun C melakukan ini di beberapa tempat lain. Sebagai contoh,static
memiliki tujuan yang berbeda dalam konteks yang berbeda. Jelas ada preseden dalam bahasa C untuk menggunakan kembali kata kunci dengan cara ini, terlepas dari apa yang dipikirkan orang tentang praktik tersebut.Java dan C # cukup berbeda untuk membuat jeda bersih untuk memperbaiki beberapa masalah ini. Java dan "aman" C # juga tidak mengizinkan pointer baku dan tidak perlu kompatibilitas C mudah (tidak aman C # tidak memungkinkan pointer tetapi sebagian besar C # kode tidak termasuk dalam kategori ini). Ini memungkinkan mereka untuk mengubah segalanya sedikit tanpa khawatir tentang kompatibilitas mundur. Salah satu cara untuk melakukan ini adalah memperkenalkan kelas
Object
pada akar hierarki dari mana semua kelas mewarisi, sehinggaObject
referensi melayani fungsi yang samavoid*
tanpa nastiness masalah jenis dan manajemen memori mentah.sumber
They also do not allow raw pointers and do not need easy C compatibility.
Salah. C # memiliki pointer. Mereka biasanya (dan biasanya harus), tetapi mereka ada di sana.void
: alasan yang jelas adalah untuk menghindari memasukkan kata kunci baru (dan berpotensi merusak program yang ada). Seandainya mereka memperkenalkan kata kunci khusus (mis.unknown
), Makavoid*
akan menjadi konstruksi yang tidak bermakna (dan mungkin ilegal), danunknown
akan sah hanya dalam bentukunknown*
.void *
,void
berarti "tidak ada", bukan "apa pun". Anda tidak dapat melakukan dereferensi avoid *
, Anda harus terlebih dahulu melemparkannya ke jenis pointer lain.void
danvoid*
tipe yang berbeda (sebenarnya,void
"tidak ada tipe"). Masuk akal juga mengatakan "int
inint*
berarti sesuatu". Tidak, begitu Anda mendeklarasikan variabel pointer, Anda mengatakan "ini adalah alamat memori yang mampu menyimpan sesuatu." Dalam kasusvoid*
, Anda mengatakan "alamat ini menyimpan sesuatu tetapi sesuatu tidak dapat disimpulkan dari jenis pointer."void
danvoid*
ada dua hal yang berbeda.void
di C berarti hal yang persis sama dengan di Jawa, tidak adanya nilai kembali. Avoid*
adalah pointer dengan tidak adanya tipe.Semua petunjuk dalam C harus dapat direferensikan. Jika Anda melakukan dereferensi
void*
, tipe apa yang akan Anda dapatkan? Ingat pointer C tidak membawa informasi tipe runtime, jadi tipenya harus diketahui pada waktu kompilasi.Mengingat konteks itu, satu-satunya hal yang dapat Anda lakukan secara logis dengan dereferensi
void*
adalah mengabaikannya, yang persis perilaku yang ditunjukkan olehvoid
tipe tersebut.sumber
gcc
tidak setuju dengan Anda. Jika Anda mencoba menggunakan nilai, buatgcc
pesan kesalahan yang mengatakanerror: void value not ignored as it ought to be
.Mungkin akan lebih berguna untuk dianggap
void
sebagai tipe pengembalian. Kemudian metode kedua Anda akan membaca "metode yang mengembalikan pointer yang tidak diketik."sumber
void
kira-kira setara dengan*
bahasa seperti ActionScript 3 yang hanya berarti "semua tipe pengembalian."void
itu bukan wildcard. Pointer hanyalah alamat memori. Menempatkan suatu tipe sebelum tulisan*
"di sini adalah tipe data yang dapat Anda temukan di alamat memori yang dirujuk oleh pointer ini." Meletakkanvoid
sebelum*
kata ke kompiler "Saya tidak tahu jenisnya; kembalikan saja pointer mentahnya, dan saya akan mencari tahu apa yang harus dilakukan dengan data yang ditunjukkannya."void*
poin pada "kekosongan". Sebuah pointer kosong tanpa menunjuk apa pun. Tetap Anda bisa melemparkannya seperti pointer lainnya.Mari kita kencangkan terminologinya sedikit.
Dari standar C 2011 online :
Sebuah
void
ekspresi tidak memiliki nilai (meskipun mungkin memiliki efek samping). Jika saya memiliki fungsi yang didefinisikan untuk kembalivoid
, seperti:lalu telepon
tidak mengevaluasi ke suatu nilai; Saya tidak dapat menetapkan hasil untuk apa pun, karena tidak ada hasil.
Sebuah pointer ke
void
dasarnya adalah "generik" jenis pointer; Anda dapat menetapkan nilaivoid *
untuk setiap jenis penunjuk objek lain tanpa perlu pemeran eksplisit (itulah sebabnya semua programmer C akan berteriak kepada Anda untuk memberikan hasilmalloc
).Anda tidak dapat secara langsung dereferensi a
void *
; Anda harus terlebih dahulu menetapkannya ke jenis penunjuk objek yang berbeda sebelum Anda dapat mengakses objek runcing-ke.void
pointer digunakan untuk mengimplementasikan antarmuka generik (kurang lebih); contoh kanonik adalahqsort
fungsi pustaka, yang dapat mengurutkan array jenis apa pun, asalkan Anda menyediakan fungsi perbandingan yang sadar jenis.Ya, menggunakan kata kunci yang sama untuk dua konsep yang berbeda (tidak ada nilai vs penunjuk umum) membingungkan, tetapi tidak seperti tidak ada preseden;
static
memiliki banyak arti berbeda baik dalam C dan C ++.sumber
Pernyataan itu benar. Itu benar untuk C dan C ++ juga.
Pernyataan itu salah.
void
sebagai tipe pengembalian di C atau C ++ berarti hal yang sama yang dilakukannya di C # dan Java. Anda bingungvoid
denganvoid*
. Mereka sangat berbeda.Ya.
Sebuah pointer adalah nilai yang dapat dereferenced . Dereferencing pointer yang valid memberikan lokasi penyimpanan tipe menunjuk-ke . Sebuah pointer kekosongan adalah pointer yang tidak memiliki tertentu menunjuk-mengetik; itu harus dikonversi ke jenis pointer yang lebih spesifik sebelum nilainya didereferensi untuk menghasilkan lokasi penyimpanan .
Java tidak memiliki void pointer; C # tidak. Mereka sama seperti di C dan C ++ - nilai pointer yang tidak memiliki tipe spesifik yang terkait dengannya, yang harus dikonversi ke tipe yang lebih spesifik sebelum mendereferensi untuk menghasilkan lokasi penyimpanan.
Pertanyaannya tidak jelas karena mengandaikan kepalsuan. Mari kita tanyakan beberapa pertanyaan yang lebih baik.
Untuk terbiasa dengan programmer yang datang ke Jawa atau C # dari bahasa di mana
void
adalah tipe pengembalian.Untuk terbiasa dengan programmer yang datang ke C # dari bahasa di mana
void*
adalah jenis pointer.sumber
Fungsi pertama tidak mengembalikan apa pun. Fungsi kedua mengembalikan pointer kosong. Saya akan menyatakan bahwa fungsi kedua sebagai
Jika mungkin membantu jika Anda menganggap "void" sebagai makna "tanpa makna". Nilai kembalinya
describe
adalah "tanpa makna". Mengembalikan nilai dari fungsi seperti itu ilegal karena Anda memberi tahu kompiler bahwa nilai kembali tidak berarti. Anda tidak dapat menangkap nilai kembali dari fungsi itu karena itu tanpa makna. Anda tidak dapat mendeklarasikan variabel tipevoid
karena itu tanpa makna. Sebagai contohvoid nonsense;
adalah omong kosong dan sebenarnya ilegal. Perhatikan juga bahwa array batalvoid void_array[42];
juga tidak masuk akal.Pointer untuk membatalkan (
void*
) adalah sesuatu yang berbeda. Ini adalah pointer, bukan array. Bacavoid*
artinya penunjuk yang menunjuk ke sesuatu "tanpa makna". Pointer adalah "tanpa makna", yang berarti tidak masuk akal untuk merujuk pointer seperti itu. Mencoba melakukannya sebenarnya ilegal. Kode yang dereferensi pointer kosong tidak akan dikompilasi.Jadi, jika Anda tidak bisa melakukan dereferensi pointer kosong, dan Anda tidak bisa membuat array void, bagaimana Anda bisa menggunakannya? Jawabannya adalah pointer ke tipe apa pun dapat dilemparkan ke dan dari
void*
. Casting beberapa pointer kevoid*
dan casting kembali ke pointer ke tipe asli akan menghasilkan nilai yang sama dengan pointer asli. Standar C menjamin perilaku ini. Alasan utama Anda melihat begitu banyak pointer kosong di C adalah bahwa kemampuan ini menyediakan cara untuk mengimplementasikan penyembunyian informasi dan pemrograman berbasis objek dalam bahasa yang sangat berorientasi objek.Akhirnya, alasan yang sering Anda lihat
move
dinyatakan sebagaivoid *move(...)
bukanvoid* move(...)
karena meletakkan tanda bintang di sebelah nama daripada tipe adalah praktik yang sangat umum di C. Alasannya adalah bahwa deklarasi berikut akan membuat Anda dalam masalah besar:int* iptr, jptr;
Ini akan membuat Anda pikir Anda mendeklarasikan dua pointer ke sebuahint
. Kamu bukan. Deklarasi ini membuatjptr
sebuahint
daripada pointer keint
. Deklarasi yang tepat adalahint *iptr, *jptr;
Anda dapat berpura-pura tanda bintang milik jenis jika Anda tetap berpegang pada praktik hanya mendeklarasikan satu variabel per pernyataan deklarasi. Namun perlu diingat bahwa Anda berpura-pura.sumber
void* null_ptr = 0;
).Sederhananya, tidak ada perbedaan . Biarkan saya memberi Anda beberapa contoh.
Katakanlah saya memiliki kelas yang disebut Mobil.
Saya percaya di sini kebingungan di sini didasarkan dari bagaimana Anda mendefinisikan
void
vsvoid*
. Tipe pengembalianvoid
berarti "Saya tidak mengembalikan apa-apa", sedangkan jenis kembalivoid*
berarti "Saya mengembalikan pointer jenis apa-apa".Ini disebabkan oleh fakta bahwa pointer adalah case khusus. Objek pointer akan selalu menunjuk ke lokasi di memori, apakah ada objek yang valid di sana atau tidak. Apakah
void* car
referensi saya byte, kata, Mobil, atau sepeda tidak masalah karenavoid*
poin saya untuk sesuatu tanpa jenis yang ditentukan. Terserah kita untuk menentukannya nanti.Dalam bahasa seperti C # dan Java, memori dikelola dan konsep pointer dimasukkan ke kelas Object. Alih-alih memiliki referensi ke objek bertipe "tidak ada", kita tahu bahwa setidaknya objek kita bertipe "Objek". Biarkan saya jelaskan alasannya.
Dalam C / C ++ konvensional jika Anda membuat objek dan menghapus pointer-nya, maka tidak mungkin untuk mendapatkan akses ke objek itu. Inilah yang dikenal sebagai kebocoran memori .
Di C # / Java, semuanya adalah objek dan ini memungkinkan runtime untuk melacak setiap objek. Tidak perlu tahu bahwa objek saya adalah Mobil, hanya perlu mengetahui beberapa informasi dasar yang sudah dijaga oleh kelas Object.
Bagi kita programmer, itu berarti bahwa banyak informasi yang harus kita tangani secara manual di C / C ++ diurus untuk kita oleh kelas Object, menghilangkan kebutuhan untuk case khusus yaitu pointer.
sumber
Saya akan mencoba mengatakan bagaimana seseorang dapat memikirkan hal-hal ini dalam C. Bahasa resmi dalam standar C tidak benar-benar nyaman
void
, dan saya tidak akan mencoba untuk sepenuhnya konsisten dengannya.Tipe
void
ini bukan warga negara kelas satu di antara tipe-tipe tersebut. Meskipun ini adalah tipe objek, itu tidak bisa menjadi tipe objek apa pun (atau nilai), bidang, atau parameter fungsi; namun itu bisa menjadi tipe kembalinya suatu fungsi, dan dengan demikian menjadi tipe ekspresi (pada dasarnya dari panggilan fungsi tersebut, tetapi ekspresi yang dibentuk dengan operator bersyarat juga dapat memiliki tipe batal). Tetapi bahkan penggunaan minimalvoid
sebagai tipe nilai yang di atas membiarkan pintu terbuka, yaitu mengakhiri fungsi yang kembalivoid
dengan pernyataan bentuk direturn E;
manaE
ekspresi tipevoid
, secara eksplisit dilarang dalam C (diizinkan dalam C ++, tetapi karena alasan tidak berlaku untuk C).Jika objek bertipe
void
diizinkan, mereka akan memiliki 0 bit (yaitu jumlah informasi dalam nilai ekspresivoid
tipe); itu tidak akan menjadi masalah besar yang memungkinkan objek seperti itu, tetapi mereka akan menjadi agak tidak berguna (objek berukuran 0 akan memberikan beberapa kesulitan dengan mendefinisikan aritmatika pointer, yang mungkin sebaiknya hanya dilarang). Himpunan nilai yang berbeda dari objek seperti itu akan memiliki satu (sepele) elemen; nilainya dapat diambil, sepele karena tidak memiliki substansi apa pun, tetapi tidak dapat dimodifikasi (tidak memiliki nilai yang berbeda ). Oleh karena itu standar ini bingung dalam mengatakanvoid
terdiri dari seperangkat nilai kosong; tipe seperti itu bisa berguna untuk menggambarkan ekspresi itu tidak bisadievaluasi (seperti lompatan atau panggilan non-terminasi) meskipun bahasa C sebenarnya tidak menggunakan tipe seperti itu.void
Dilarang sebagai tipe nilai, telah digunakan dalam setidaknya dua cara yang tidak berhubungan langsung untuk menunjuk "terlarang": menetapkan(void)
sebagai spesifikasi parameter dalam tipe fungsi berarti memberikan argumen kepada mereka dilarang (sementara '()' sebaliknya akan berarti semuanya diperbolehkan, dan ya, bahkan memberikanvoid
ekspresi sebagai argumen untuk fungsi dengan(void)
spesifikasi parameter dilarang), dan menyatakanvoid *p
berarti bahwa ekspresi dereferencing*p
dilarang (tetapi bukan bahwa itu adalah ekspresi tipe yang validvoid
). Jadi Anda benar bahwa penggunaanvoid
ini tidak benar-benar konsistenvoid
jenis ekspresi yang valid. Namun objek bertipevoid*
sebenarnya bukan penunjuk ke nilai jenis apa pun, itu berarti nilai yang diperlakukan sebagai penunjuk meskipun tidak dapat ditereferensi dan penunjuk aritmatika dengannya dilarang. "Diperlakukan sebagai pointer" kemudian benar-benar hanya berarti dapat dilemparkan dari dan ke semua jenis pointer tanpa kehilangan informasi. Namun itu juga dapat digunakan untuk melemparkan nilai integer ke dan dari, sehingga tidak harus mengarah ke apa pun.sumber
void*
jenis pointer lain mungkin kehilangan informasi (atau bahkan memiliki UB, saya tidak dapat mengingatnya), tetapi jika nilaivoid*
berasal dari casting sebuah pointer dari jenis yang sama dengan yang sekarang Anda casting, maka tidak.Dalam C # dan Java, setiap kelas diturunkan dari suatu
Object
kelas. Karenanya kapan pun kita ingin memberikan referensi ke "sesuatu", kita dapat menggunakan referensi tipeObject
.Karena kita tidak perlu menggunakan void pointer untuk tujuan itu dalam bahasa-bahasa ini,
void
tidak harus berarti "sesuatu atau tidak sama sekali" di sini.sumber
Dalam C, dimungkinkan untuk memiliki pointer ke hal-hal dari tipe apa pun [pointer berperilaku sebagai tipe referensi]. Pointer dapat mengidentifikasi objek dari tipe yang dikenal, atau dapat mengidentifikasi objek dengan tipe yang arbitrer (tidak diketahui). Pembuat C memilih untuk menggunakan sintaksis umum yang sama untuk "penunjuk ke jenis sembarang jenis" seperti untuk "penunjuk ke jenis jenis tertentu". Karena sintaks dalam kasus terakhir memerlukan token untuk menentukan apa jenisnya, sintaks sebelumnya mengharuskan meletakkan sesuatu di mana token itu akan berada jika jenis itu diketahui. C memilih untuk menggunakan kata kunci "void" untuk tujuan itu.
Dalam Pascal, seperti halnya dengan C, dimungkinkan untuk memiliki petunjuk tentang hal-hal apa pun; dialek yang lebih populer dari Pascal juga memungkinkan "penunjuk ke jenis yang sewenang-wenang", tetapi alih-alih menggunakan sintaks yang sama seperti yang mereka gunakan untuk "penunjuk ke jenis jenis tertentu", mereka hanya menyebut yang pertama sebagai
Pointer
.Di Jawa, tidak ada tipe variabel yang ditentukan pengguna; semuanya adalah referensi objek primitif atau heap. Seseorang dapat mendefinisikan hal-hal dari tipe "referensi ke objek tumpukan tipe tertentu" atau "referensi ke objek tumpukan tipe sewenang-wenang". Yang pertama hanya menggunakan nama jenis tanpa tanda baca khusus untuk menunjukkan bahwa itu adalah referensi (karena tidak bisa apa pun); yang terakhir menggunakan nama tipe
Object
.Penggunaan
void*
sebagai nomenklatur untuk pointer ke sesuatu yang jenis sewenang-wenang sejauh yang saya tahu unik untuk C dan turunan langsung seperti C ++; bahasa lain yang saya tahu menangani konsep dengan hanya menggunakan jenis yang diberi nama berbeda.sumber
Anda dapat memikirkan kata kunci
void
seperti kosongstruct
( Dalam C99, struct kosong tampaknya tidak diizinkan , dalam .NET dan C ++ kata kunci tersebut mengambil lebih dari 0 memori, dan terakhir saya ingat semua yang ada di Jawa adalah kelas):... yang artinya adalah tipe dengan panjang 0. Ini adalah cara kerjanya ketika Anda melakukan panggilan fungsi yang mengembalikan
void
... alih-alih mengalokasikan 4 byte atau lebih di stack untuk nilai pengembalian integer, itu tidak mengubah penumpukan stack sama sekali (menambahnya dengan panjang 0) ).Jadi, jika Anda mendeklarasikan beberapa variabel seperti:
... dan kemudian Anda mengambil alamat variabel-variabel itu, maka mungkin
b
danc
mungkin terletak di tempat yang sama dalam memori, karenab
memiliki panjang nol. Jadi avoid*
adalah pointer di memori ke tipe built-in dengan panjang nol. Fakta bahwa Anda mungkin tahu bahwa ada sesuatu segera setelah itu yang mungkin berguna bagi Anda adalah masalah lain, dan mengharuskan Anda untuk benar-benar tahu apa yang Anda lakukan (dan berbahaya).sumber