Saya selalu bertanya-tanya ini - mengapa Anda tidak dapat mendeklarasikan variabel setelah label kasus dalam pernyataan switch? Di C ++ Anda dapat mendeklarasikan variabel cukup banyak di mana saja (dan mendeklarasikannya mendekati penggunaan pertama jelas merupakan hal yang baik) tetapi hal berikut masih tidak berfungsi:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Di atas memberi saya kesalahan berikut (MSC):
inisialisasi 'newVal' dilewati oleh label 'case'
Ini tampaknya juga menjadi batasan dalam bahasa lain. Mengapa ini menjadi masalah?
c++
switch-statement
rampok
sumber
sumber
Jawaban:
Case
hanya pernyataan label . Ini berarti kompiler akan menafsirkan ini sebagai lompatan langsung ke label. Dalam C ++, masalah di sini adalah salah satu ruang lingkup. Kurung keriting Anda menentukan ruang lingkup sebagai segala sesuatu di dalamswitch
pernyataan. Ini berarti bahwa Anda memiliki ruang lingkup di mana lompatan akan dilakukan lebih lanjut ke dalam kode yang melewatkan inisialisasi.Cara yang benar untuk menangani ini adalah dengan menentukan ruang lingkup khusus untuk
case
pernyataan itu dan mendefinisikan variabel Anda di dalamnya:sumber
Pertanyaan
iniawalnya ditandai sebagai [C] dan [C ++] secara bersamaan. Kode asli memang tidak valid di C dan C ++, tetapi untuk alasan yang tidak terkait sama sekali berbeda.Dalam C ++ kode ini tidak valid karena
case ANOTHER_VAL:
label melompat ke dalam lingkup variabelnewVal
melewati inisialisasi. Melompati pemintas inisialisasi objek otomatis adalah ilegal di C ++. Sisi masalah ini ditangani dengan benar oleh sebagian besar jawaban.Namun, dalam bahasa C melewati inisialisasi variabel bukan kesalahan. Melompat ke ruang lingkup variabel atas inisialisasi adalah legal dalam C. Ini berarti bahwa variabel dibiarkan tidak diinisialisasi. Kode asli tidak dikompilasi dalam C karena alasan yang sama sekali berbeda. Label
case VAL:
dalam kode asli dilampirkan pada deklarasi variabelnewVal
. Dalam bahasa C, deklarasi bukan pernyataan. Mereka tidak dapat diberi label. Dan inilah yang menyebabkan kesalahan ketika kode ini ditafsirkan sebagai kode C.Menambahkan
{}
blok tambahan memperbaiki masalah C ++ dan C, meskipun masalah ini sangat berbeda. Di sisi C ++ itu membatasi ruang lingkupnewVal
, memastikan bahwacase ANOTHER_VAL:
tidak lagi melompat ke ruang lingkup itu, yang menghilangkan masalah C ++. Di sisi C yang ekstra{}
memperkenalkan pernyataan majemuk, sehingga membuatcase VAL:
label untuk diterapkan pada pernyataan, yang menghilangkan masalah C.Dalam kasus C masalah dapat dengan mudah diselesaikan tanpa
{}
. Cukup tambahkan pernyataan kosong setelahcase VAL:
label dan kode akan menjadi validPerhatikan bahwa meskipun sekarang valid dari sudut pandang C, tetap tidak valid dari sudut pandang C ++.
Secara simetris, dalam kasus C ++ masalah dapat dengan mudah diselesaikan tanpa
{}
. Hapus saja penginisialisasi dari deklarasi variabel dan kode akan menjadi validPerhatikan bahwa meskipun sekarang valid dari sudut pandang C ++, tetap tidak valid dari sudut pandang C.
sumber
newVal
ketika melompat keANOTHER_VAL
?case ANOTHER_VAL:
titik variabelnewVal
terlihat, tetapi dengan nilai tak tentu.§A9.3: Compound Statement
dari K&R C (edisi kedua). Entri menyebutkan definisi teknis dari pernyataan majemuk yang{declaration-list[opt] statement-list[opt]}
. Bingung, karena saya pikir deklarasi adalah pernyataan, saya mencarinya dan segera menemukan pertanyaan ini, contoh di mana perbedaan itu menjadi jelas dan benar-benar merusak sebuah program. Saya percaya solusi lain (untuk C) akan menempatkan pernyataan lain (mungkin pernyataan nol?) Sebelum deklarasi sehingga pernyataan berlabel puas.Baik. Hanya untuk memperjelas hal ini tidak ada hubungannya dengan deklarasi. Ini hanya berkaitan dengan "melompati inisialisasi" (ISO C ++ '03 6.7 / 3)
Banyak posting di sini telah menyebutkan bahwa melompati deklarasi dapat mengakibatkan variabel "tidak dideklarasikan". Ini tidak benar. Objek POD dapat dideklarasikan tanpa penginisialisasi tetapi akan memiliki nilai tak tentu. Sebagai contoh:
Di mana objek adalah non-POD atau agregat kompiler secara implisit menambahkan penginisialisasi, dan sehingga tidak mungkin untuk melompati deklarasi seperti itu:
Batasan ini tidak terbatas pada pernyataan switch. Ini juga merupakan kesalahan untuk menggunakan 'goto' untuk melompati inisialisasi:
Sedikit hal-hal sepele adalah bahwa ini adalah perbedaan antara C ++ dan C. Dalam C, itu bukan kesalahan untuk melompati inisialisasi.
Seperti yang telah disebutkan orang lain, solusinya adalah dengan menambahkan blok bersarang sehingga masa pakai variabel terbatas pada label kasus individu.
sumber
Seluruh pernyataan sakelar berada dalam cakupan yang sama. Untuk menyiasatinya, lakukan ini:
Perhatikan tanda kurung.
sumber
Setelah membaca semua jawaban dan beberapa penelitian lagi saya mendapatkan beberapa hal.
Dalam C, sesuai dengan spesifikasi,
§6.8.1 Pernyataan Berlabel:
Di C tidak ada klausa yang memungkinkan untuk "deklarasi berlabel". Itu bukan bagian dari bahasa.
Begitu
Ini tidak akan dikompilasi , lihat http://codepad.org/YiyLQTYw . GCC memberi kesalahan:
Bahkan
ini juga bukan kompilasi , lihat http://codepad.org/BXnRD3bu . Di sini saya juga mendapatkan kesalahan yang sama.
Dalam C ++, sesuai dengan spesifikasi,
label-deklarasi diizinkan tetapi label-inisialisasi tidak diizinkan.
Lihat http://codepad.org/ZmQ0IyDG .
Solusi untuk kondisi seperti itu adalah dua
Gunakan ruang lingkup baru menggunakan {}
Atau gunakan pernyataan dummy dengan label
Deklarasikan variabel sebelum beralih () dan inisialisasi dengan nilai yang berbeda dalam pernyataan kasus jika memenuhi kebutuhan Anda
Beberapa hal lagi dengan pernyataan switch
Jangan pernah menulis pernyataan apa pun di sakelar yang bukan merupakan bagian dari label apa pun, karena tidak akan pernah dieksekusi:
Lihat http://codepad.org/PA1quYX3 .
sumber
a
ke dalam lingkup variabela
. Jadi, dari sudut pandang C, masalahnya ada padacase VAL:
label dan Anda menggambarkannya dengan benar. Tapi dari sudut pandang C ++, masalahnya ada padacase ANOTHER_VAL:
label.Anda tidak dapat melakukan ini, karena
case
label sebenarnya hanya titik masuk ke blok yang berisi.Ini paling jelas digambarkan oleh perangkat Duff . Berikut beberapa kode dari Wikipedia:
Perhatikan bagaimana
case
label benar-benar mengabaikan batas blok. Ya, ini jahat. Tapi ini sebabnya contoh kode Anda tidak berfungsi. Melompat kecase
label sama dengan menggunakangoto
, jadi Anda tidak diizinkan melompati variabel lokal dengan konstruktor.Seperti yang ditunjukkan oleh beberapa poster lain, Anda harus membuat blok sendiri:
sumber
SAR
dalam x86, dibandingkanSHR
yang untuk shift yang tidak ditandatangani).Sebagian besar jawaban sejauh ini salah dalam satu hal: Anda dapat mendeklarasikan variabel setelah pernyataan kasus, tetapi Anda tidak dapat menginisialisasi mereka:
Seperti yang disebutkan sebelumnya, cara yang bagus untuk mengatasi ini adalah menggunakan kawat gigi untuk membuat ruang lingkup untuk kasus Anda.
sumber
Trik beralih kejahatan favorit saya adalah menggunakan if (0) untuk melewati label case yang tidak diinginkan.
Tapi sangat jahat.
sumber
goto
tanpa kebingungan kodeCoba ini:
sumber
Anda bisa mendeklarasikan variabel dalam pernyataan sakelar jika Anda memulai blok baru:
Alasannya adalah karena mengalokasikan (dan mengklaim kembali) ruang pada stack untuk penyimpanan variabel lokal.
sumber
Mempertimbangkan:
Dengan tidak adanya pernyataan break, terkadang newVal dideklarasikan dua kali, dan Anda tidak tahu apakah itu sampai runtime. Dugaan saya adalah bahwa batasannya adalah karena jenis kebingungan ini. Akan seperti apa ruang lingkup newVal? Konvensi akan menentukan bahwa itu akan menjadi seluruh blok saklar (antara kawat gigi).
Saya bukan programmer C ++, tetapi di C:
Bekerja dengan baik. Mendeklarasikan variabel di dalam blok switch tidak masalah. Mendeklarasikan setelah penjaga kasus tidak.
sumber
Seluruh bagian dari saklar adalah konteks deklarasi tunggal. Anda tidak dapat mendeklarasikan variabel dalam pernyataan kasus seperti itu. Coba ini sebagai gantinya:
sumber
Jika kode Anda mengatakan "int newVal = 42" maka Anda cukup berharap bahwa newVal tidak pernah diinisialisasi. Tetapi jika Anda kebagian pernyataan ini (yang sedang Anda lakukan) maka itulah yang terjadi - newVal berada dalam lingkup tetapi belum ditugaskan.
Jika itu yang Anda inginkan terjadi maka bahasa tersebut harus membuatnya eksplisit dengan mengatakan "int newVal; newVal = 42;". Kalau tidak, Anda dapat membatasi ruang lingkup newVal untuk satu kasus, yang lebih mungkin apa yang Anda inginkan.
Ini dapat mengklarifikasi hal-hal jika Anda mempertimbangkan contoh yang sama tetapi dengan "const int newVal = 42;"
sumber
Saya hanya ingin menekankan ramping 's titik . Konstruk saklar menciptakan ruang lingkup seluruh warga negara kelas satu. Jadi, dimungkinkan untuk mendeklarasikan (dan menginisialisasi) variabel dalam pernyataan switch sebelum label kasus pertama, tanpa pasangan braket tambahan:
sumber
int newVal
akan dieksekusi, tetapi bukan= 42
penugasan.Sejauh ini jawabannya adalah untuk C ++.
Untuk C ++, Anda tidak dapat melompati inisialisasi. Anda bisa dalam C. Namun, dalam C, deklarasi bukan pernyataan, dan label kasus harus diikuti oleh pernyataan.
Jadi, valid (tapi jelek) C, C ++ tidak valid
Sebaliknya, dalam C ++, deklarasi adalah pernyataan, jadi berikut ini adalah C ++ yang valid, C yang tidak valid
sumber
Menarik bahwa ini baik-baik saja:
... tapi ini bukan:
Saya mendapatkan bahwa perbaikannya cukup sederhana, tapi saya belum mengerti mengapa contoh pertama tidak mengganggu kompiler. Seperti yang disebutkan sebelumnya (2 tahun sebelumnya hehe), deklarasi bukanlah yang menyebabkan kesalahan, meskipun logika. Inisialisasi adalah masalahnya. Jika variabel diinisialisasi dan dideklarasikan pada baris yang berbeda, itu dikompilasi.
sumber
Saya menulis jawaban ini awalnya untuk pertanyaan ini . Namun ketika saya selesai saya menemukan bahwa jawaban telah ditutup. Jadi saya mempostingnya di sini, mungkin seseorang yang suka referensi ke standar akan merasa terbantu.
Kode Asli yang dimaksud:
Sebenarnya ada 2 pertanyaan:
1. Mengapa saya dapat mendeklarasikan variabel setelah
case
label?Itu karena dalam label C ++ harus dalam bentuk:
N3337 6.1 / 1
Dan dalam
C++
pernyataan pernyataan juga dianggap sebagai pernyataan (berlawanan denganC
):N3337 6/1:
2. Mengapa saya bisa melompati deklarasi variabel dan kemudian menggunakannya?
Karena: N3337 6.7 / 3
Sejak
k
adalah tipe skalar , dan tidak dimulai pada titik deklarasi melompat lebih deklarasi itu adalah mungkin. Ini setara secara semantik:Namun itu tidak akan mungkin, jika
x
diinisialisasi pada titik pernyataan:sumber
Variabel-variabel baru dapat didekati hanya pada lingkup blok. Anda perlu menulis sesuatu seperti ini:
Tentu saja, newVal hanya memiliki ruang ...
Ceria, Ralph
sumber
Sebuah
switch
blok tidak sama sebagai suksesiif/else if
blok. Saya terkejut tidak ada jawaban lain yang menjelaskannya dengan jelas.Pertimbangkan
switch
pernyataan ini :Mungkin mengejutkan, tetapi kompiler tidak akan melihatnya sebagai sederhana
if/else if
. Ini akan menghasilkan kode berikut:The
case
pernyataan diubah menjadi label dan kemudian disebut dengangoto
. Tanda kurung membuat lingkup baru dan mudah untuk melihat sekarang mengapa Anda tidak dapat mendeklarasikan dua variabel dengan nama yang sama di dalam aswitch
blok.Ini mungkin terlihat aneh, tetapi perlu untuk mendukung fallthrough (yaitu, tidak menggunakan
break
untuk membiarkan eksekusi berlanjut ke yang berikutnyacase
).sumber
Saya percaya masalahnya adalah bahwa pernyataan itu dilewati, dan Anda mencoba menggunakan var di tempat lain, itu tidak akan dinyatakan.
sumber
newVal ada di seluruh ruang lingkup switch tetapi hanya diinisialisasi jika ekstremitas VAL dipukul. Jika Anda membuat blok di sekitar kode dalam VAL, seharusnya OK.
sumber
C ++ Standard memiliki: Dimungkinkan untuk mentransfer ke dalam blok, tetapi tidak dengan cara yang melewati deklarasi dengan inisialisasi. Suatu program yang melompat dari titik di mana variabel lokal dengan durasi penyimpanan otomatis tidak dalam ruang lingkup ke titik di mana ruang lingkupnya tidak terbentuk kecuali variabel tersebut memiliki tipe POD (3.9) dan dideklarasikan tanpa penginisialisasi (8.5).
Kode untuk menggambarkan aturan ini:
Kode untuk menunjukkan efek penginisialisasi:
sumber
Tampaknya objek anonim dapat dideklarasikan atau dibuat dalam pernyataan kasus sakelar untuk alasan bahwa mereka tidak dapat dirujuk dan karenanya tidak dapat diteruskan ke kasing kasus berikutnya. Pertimbangkan contoh ini kompilasi pada GCC 4.5.3 dan Visual Studio 2008 (mungkin merupakan masalah kepatuhan sehingga para ahli mempertimbangkan)
sumber
const
referensi dengan cakupannya sendiri). Ini adalah ekspresi yang hidup dan mati dalam pernyataannya (di mana pun itu berada). Karena itu, sama sekali tidak relevan.Foo();
bukan deklarasi; pertanyaannya adalah tentang deklarasi.