Mengapa mendeklarasikan variabel dalam satu baris, dan menetapkannya di baris berikutnya?

101

Saya sering melihat dalam kode C dan C ++ konvensi berikut:

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

dari pada

some_type val = something;
some_type *ptr = &something_else;

Awalnya saya berasumsi bahwa ini adalah kebiasaan yang tersisa dari hari-hari ketika Anda harus mendeklarasikan semua variabel lokal di bagian atas ruang lingkup. Tapi saya sudah belajar untuk tidak begitu cepat mengabaikan kebiasaan pengembang veteran. Jadi, apakah ada alasan bagus untuk menyatakan dalam satu baris, dan menetapkan setelahnya?

Jonathan Sterling
sumber
12
+1 untuk "Saya telah belajar untuk tidak mengabaikan kebiasaan pengembang veteran dengan begitu cepat." Itu pelajaran yang bijak untuk dipelajari.
Wildcard

Jawaban:

92

C

Dalam C89 semua deklarasi harus berada di awal lingkup ( { ... }), tetapi persyaratan ini dibatalkan dengan cepat (pertama dengan ekstensi kompiler dan kemudian dengan standar).

C ++

Contoh-contoh ini tidak sama. some_type val = something;memanggil copy constructor saat val = something;memanggil constructor default dan kemudian operator=fungsinya Perbedaan ini sering kritis.

Kebiasaan

Beberapa orang lebih suka mendeklarasikan variabel terlebih dahulu dan kemudian mendefinisikannya, dalam kasus mereka memformat ulang kode mereka nanti dengan deklarasi di satu tempat dan definisi di tempat lain.

Tentang pointer, beberapa orang memiliki kebiasaan untuk menginisialisasi setiap pointer ke NULLatau nullptr, tidak peduli apa yang mereka lakukan dengan pointer itu.

orlp
sumber
1
Perbedaan besar untuk C ++, terima kasih. Bagaimana dengan di C polos?
Jonathan Sterling
13
Fakta bahwa MSVC masih tidak mendukung deklarasi kecuali pada awal blok ketika dikompilasi dalam mode C adalah sumber iritasi tanpa akhir bagi saya.
Michael Burr
5
@Michael Burr: Ini karena MSVC tidak mendukung C99 sama sekali.
orlp
3
"some_type val = sesuatu; panggil konstruktor salin": mungkin memanggil konstruktor salin, tetapi Standar memungkinkan kompiler untuk menghilangkan konstruksi default tempory, menyalin konstruksi val dan penghancuran sementara dan langsung membangun val menggunakan sebuah some_typekonstruktor mengambil somethingsebagai argumen tunggal. Ini adalah kasus tepi yang sangat menarik dan tidak biasa di C ++ ... itu berarti ada anggapan tentang makna semantik dari operasi ini.
2
@Aerovistae: untuk tipe bawaan mereka sama, tetapi sama tidak selalu bisa dikatakan untuk tipe yang ditentukan pengguna.
orlp
27

Anda telah menandai pertanyaan C dan C ++ secara bersamaan, sementara jawabannya sangat berbeda dalam bahasa-bahasa ini.

Pertama, kata-kata dari judul pertanyaan Anda salah (atau, lebih tepatnya, tidak relevan dengan pertanyaan itu sendiri). Dalam kedua contoh Anda, variabel dideklarasikan dan didefinisikan secara bersamaan, dalam satu baris. Perbedaan antara contoh Anda adalah bahwa pada variabel pertama dibiarkan tidak diinisialisasi atau diinisialisasi dengan nilai dummy dan kemudian diberi nilai yang bermakna kemudian. Dalam contoh kedua variabel diinisialisasi segera.

Kedua, dalam bahasa C ++, seperti yang dicatat oleh @nightcracker dalam jawabannya, kedua konstruk ini secara semantik berbeda. Yang pertama bergantung pada inisialisasi sedangkan yang kedua - pada tugas. Dalam C ++ operasi ini kelebihan beban dan karena itu berpotensi menyebabkan hasil yang berbeda (walaupun orang dapat mencatat bahwa memproduksi inisialisasi dan penugasan yang tidak setara bukanlah ide yang baik).

Dalam bahasa C standar asli (C89 / 90) adalah ilegal untuk mendeklarasikan variabel di tengah blok, itulah sebabnya Anda mungkin melihat variabel dinyatakan tidak diinisialisasi (atau diinisialisasi dengan nilai dummy) di awal blok dan kemudian ditugaskan bermakna nilai nanti, ketika nilai-nilai bermakna itu tersedia.

Dalam bahasa C99 tidak apa-apa untuk mendeklarasikan variabel di tengah-tengah blok (seperti dalam C ++), yang berarti bahwa pendekatan pertama hanya diperlukan dalam beberapa situasi tertentu ketika penginisialisasi tidak diketahui pada titik deklarasi. (Ini juga berlaku untuk C ++).

Semut
sumber
2
@ Jonathan Sterling: Saya membaca contoh Anda. Anda mungkin perlu memoles terminologi standar bahasa C dan C ++. Secara khusus, pada istilah deklarasi dan definisi , yang memiliki arti khusus dalam bahasa-bahasa ini. Saya akan mengulanginya lagi: dalam kedua contoh Anda variabel dideklarasikan dan didefinisikan dalam satu baris. Dalam C / C ++ garis some_type val;segera menyatakan dan mendefinisikan variabel val. Inilah yang saya maksud dalam jawaban saya.
1
Saya mengerti maksud Anda di sana. Anda pasti benar tentang menyatakan dan mendefinisikan menjadi agak tidak berarti seperti saya menggunakannya. Saya harap Anda menerima permintaan maaf saya untuk kata-kata buruk, dan komentar buruk.
Jonathan Sterling
1
Jadi, jika konsensusnya adalah "menyatakan" adalah kata yang salah, saya sarankan seseorang dengan pengetahuan standar yang lebih baik daripada saya mengedit halaman Wikibooks.
Jonathan Sterling
2
Dalam konteks lain mendeklarasikan akan menjadi kata yang tepat, tetapi karena menyatakan adalah konsep yang terdefinisi dengan baik, dengan konsekuensinya, dalam C dan C ++ Anda tidak dapat menggunakannya secara longgar seperti yang Anda bisa dalam konteks lain.
orlp
2
@ybungalobill: Anda salah. Deklarasi dan definisi dalam C / C ++ bukanlah konsep yang saling eksklusif. Sebenarnya, definisi hanyalah bentuk spesifik dari deklarasi . Setiap definisi adalah deklarasi pada saat bersamaan (dengan beberapa pengecualian). Ada deklarasi mendefinisikan (yaitu definisi) dan deklarasi non-mendefinisikan. Selain itu, biasanya deklarasi term digunakan sepanjang waktu (bahkan jika itu adalah definisi), kecuali untuk konteks ketika perbedaan antara keduanya sangat penting.
13

Saya pikir itu kebiasaan lama, sisa dari "deklarasi lokal" kali. Dan karena itu sebagai jawaban atas pertanyaan Anda: Tidak, saya pikir tidak ada alasan yang bagus. Saya tidak pernah melakukannya sendiri.


sumber
4

Aku mengatakan sesuatu tentang itu dalam jawaban saya untuk pertanyaan oleh Helium3 .

Pada dasarnya, saya katakan itu adalah alat bantu visual untuk dengan mudah melihat apa yang diubah.

if (a == 0) {
    struct whatever *myobject = 0;
    /* did `myobject` (the pointer) get assigned?
    ** or was it `*myobject` (the struct)? */
}

dan

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* `myobject` (the pointer) got assigned */
}
pmg
sumber
4

Jawaban lainnya cukup bagus. Ada beberapa sejarah di sekitar ini dalam C. Dalam C ++ ada perbedaan antara konstruktor dan operator penugasan.

Saya terkejut tidak ada yang menyebutkan poin tambahan: menjaga deklarasi terpisah dari penggunaan variabel kadang-kadang bisa jauh lebih mudah dibaca.

Berbicara secara visual, ketika membaca kode, artefak yang lebih biasa, seperti jenis dan nama variabel, bukan yang melompat pada Anda. Ini adalah pernyataan yang biasanya paling Anda minati, menghabiskan sebagian besar waktu menatap, dan ada kecenderungan untuk melirik sisanya.

Jika saya memiliki beberapa jenis, nama, dan penugasan semua terjadi di ruang sempit yang sama, itu sedikit kelebihan informasi. Lebih jauh, itu berarti bahwa sesuatu yang penting sedang terjadi di ruang yang biasanya saya tinjau.

Mungkin agak kontra-intuitif untuk mengatakan, tetapi ini adalah satu contoh di mana membuat sumber Anda mengambil lebih banyak ruang vertikal dapat membuatnya lebih baik. Saya melihat ini sebagai alasan mengapa Anda tidak harus menulis baris penuh sesak yang melakukan jumlah aritmatika dan tugas penunjuk yang gila di ruang vertikal yang ketat - hanya karena bahasa tersebut memungkinkan Anda lolos dengan hal-hal seperti itu tidak berarti Anda harus melakukan sepanjang waktu. :-)

asveikau
sumber
2

Dalam C, ini adalah praktik standar karena variabel harus dideklarasikan pada awal fungsi, tidak seperti di C ++, di mana ia dapat dideklarasikan di mana saja di badan fungsi yang akan digunakan setelahnya. Pointer diatur ke 0 atau NULL, karena itu hanya memastikan bahwa pointer menunjuk ke sampah. Kalau tidak, tidak ada keuntungan signifikan yang bisa saya pikirkan, yang memaksa siapa pun untuk melakukan hal itu.

Vite Falcon
sumber
2

Pro untuk definisi variabel lokalisasi dan inisialisasi bermakna mereka:

  • jika variabel biasanya diberi nilai yang berarti ketika mereka pertama kali muncul dalam kode (perspektif lain tentang hal yang sama: Anda menunda penampilan mereka sampai nilai yang bermakna tersedia) maka tidak ada kemungkinan mereka secara tidak sengaja digunakan dengan nilai yang tidak berarti atau tidak diinisialisasi ( yang dapat dengan mudah terjadi adalah beberapa inisialisasi sengaja dilewati karena pernyataan kondisional, evaluasi hubung singkat, pengecualian dll.)

  • bisa lebih efisien

    • menghindari overhead pengaturan nilai awal (konstruksi default atau inisialisasi ke beberapa nilai sentinel seperti NULL)
    • operator= terkadang bisa kurang efisien dan memerlukan objek sementara
    • kadang-kadang (terutama untuk fungsi sebaris) pengoptimal dapat menghapus beberapa / semua ketidakefisienan

  • meminimalkan ruang lingkup variabel pada gilirannya meminimalkan jumlah rata-rata variabel secara bersamaan dalam lingkup : ini

    • membuatnya lebih mudah untuk secara mental melacak variabel dalam lingkup, alur eksekusi dan pernyataan yang mungkin mempengaruhi variabel-variabel tersebut, dan impor nilainya
    • setidaknya untuk beberapa objek yang kompleks dan buram, ini mengurangi penggunaan sumber daya (tumpukan, utas, memori bersama, deskriptor) dari program
  • kadang-kadang lebih ringkas karena Anda tidak mengulangi nama variabel dalam definisi kemudian dalam tugas awal yang bermakna

  • diperlukan untuk jenis-jenis tertentu seperti referensi dan ketika Anda ingin objek menjadi const

Argumen untuk pengelompokan definisi variabel:

  • kadang-kadang lebih mudah dan / atau singkat untuk memperhitungkan tipe dari sejumlah variabel:

    the_same_type v1, v2, v3;

    (jika alasannya hanya karena nama jenisnya terlalu panjang atau rumit, typedefterkadang bisa lebih baik)

  • terkadang diinginkan untuk mengelompokkan variabel secara independen dari penggunaannya untuk menekankan sekumpulan variabel (dan tipe) yang terlibat dalam beberapa operasi:

    type v1;
    type v2; type v3;

    Ini menekankan kesamaan tipe dan membuatnya sedikit lebih mudah untuk mengubahnya, sambil tetap berpegang pada variabel per baris yang memfasilitasi copy-paste, //komentar dll.

Seperti yang sering terjadi dalam pemrograman, sementara ada manfaat empiris yang jelas untuk satu praktik dalam kebanyakan situasi, praktik lain benar-benar dapat jauh lebih baik dalam beberapa kasus.

Tony
sumber
Saya berharap lebih banyak bahasa akan membedakan kasus di mana kode menyatakan dan menetapkan nilai variabel yang tidak akan pernah ditulis di tempat lain, meskipun variabel baru dapat menggunakan nama yang sama [yaitu di mana perilaku akan sama apakah pernyataan nanti menggunakan variabel yang sama atau yang berbeda], dari kode tempat membuat variabel yang harus dapat ditulis di beberapa tempat. Sementara kedua kasus penggunaan akan menjalankan cara yang sama, mengetahui kapan variabel dapat berubah sangat membantu ketika mencoba melacak bug.
supercat