Apa penggunaan idiomatik dari blok arbitrer di C?

15

Blok adalah daftar pernyataan yang akan dieksekusi. Contoh di mana blok muncul di C adalah setelah pernyataan sementara dan di jika pernyataan

while( boolean expression)
    statement OR block

if (boolean expression)
    statement OR block

C juga memungkinkan blok untuk disarangkan di blok. Saya dapat menggunakan ini untuk menggunakan kembali nama variabel, misalkan saya sangat suka 'x'

int x = 0;
while (x < 10)
{
    {
        int x = 5;
        printf("%d",x)
    }
    x = x+1;
}

akan mencetak nomor 5 sepuluh kali. Saya kira saya bisa melihat situasi di mana menjaga jumlah nama variabel rendah diinginkan. Mungkin dalam ekspansi makro. Namun, saya tidak dapat melihat alasan sulit untuk memerlukan fitur ini. Adakah yang bisa membantu saya memahami penggunaan fitur ini dengan menyediakan beberapa idiom di mana ia digunakan.

Jonathan Gallagher
sumber
1
Jujur, saya hanya mencoba memahami sintaks C, dan saya ingin tahu.
Jonathan Gallagher
Semangat C adalah mempercayai programmer. Pemrogram memiliki kekuatan yang dimilikinya untuk membuat sesuatu yang hebat ... atau membuat sesuatu yang buruk. Secara pribadi, saya tidak suka nama variabel kelebihan beban tetapi programmer lain mungkin. Pada akhirnya jika kita mencapai apa yang seharusnya kita lakukan dengan bug minimum ... mengapa kita harus berdebat?
Fiddling Bits
1
Itulah perasaan yang saya dapatkan dari C. Saya semua untuk sintaks yang mendukung gaya pengkodean yang berbeda (juga selama semantik bahasa tersebut oke). Hanya saja ... saya melihat ini, dan tanggapan langsung saya adalah, saya bisa menerapkan transformasi sumber-ke-sumber mengubah nama semua variabel dalam blok dengan nama baru, dan meratakan blok sepenuhnya. Kapan saja saya pikir saya bisa menyingkirkan sesuatu, saya berasumsi ada sesuatu yang saya lewatkan.
Jonathan Gallagher
Waktu yang lucu, sebagai programmer non-C saya menemukan sintaks ini hari ini dalam kode C dan ingin tahu untuk apa itu. Saya senang Anda bertanya.
Brandon

Jawaban:

8

Idenya bukan untuk menjaga jumlah nama variabel rendah atau mendorong penggunaan kembali nama, tetapi untuk membatasi ruang lingkup variabel. Jika Anda memiliki:

int x = 0;
//...
{
    int y = 3;
    //...
}
//...

maka ruang lingkup yterbatas pada blok, yang berarti Anda dapat melupakannya sebelum atau setelah blok. Anda melihat ini paling sering digunakan sehubungan dengan loop dan kondisional. Anda juga melihatnya lebih sering dalam bahasa mirip C seperti C ++, di mana variabel yang keluar dari ruang lingkup menyebabkan kehancurannya.

Caleb
sumber
Saya pikir blok terjadi secara alami dengan kondisi, loop, dan fungsi tubuh. Yang saya ingin tahu adalah bahwa dalam C, saya dapat menempatkan blok di mana saja. Komentar kedua Anda tentang C ++ menarik - bahwa meninggalkan ruang lingkup menyebabkan kehancuran. Apakah ini hanya merujuk pada pengumpulan sampah, atau penggunaan berikut ini: dapatkah saya mengambil fungsi badan, yang memiliki "bagian" yang berbeda dan menggunakan blok untuk mengontrol jejak memori dengan memicu perusakan ruang variabel?
Jonathan Gallagher
1
Sejauh yang saya ketahui, C akan mengalokasikan kembali semua memori untuk semua variabel dalam fungsi. Setelah mereka keluar lingkup bagian jalan melalui suatu fungsi tidak akan memiliki manfaat kinerja dalam C. Demikian pula memiliki variabel memasuki lingkup "hanya kadang-kadang" tidak akan mengubah jejak memori selama panggilan ke fungsi
Gankro
@Gankro Hal ini dapat berdampak jika ada beberapa cakupan bersarang eksklusif. Kompiler dapat menggunakan kembali sebagian memori yang telah dialokasikan sebelumnya untuk variabel di masing-masing cakupan ini. Tentu saja, alasan mengapa hal itu tidak terlintas dalam pikiran adalah bahwa jika ruang lingkup tunggal sewenang-wenang sudah menjadi tanda Anda mungkin perlu mengekstrak ke suatu fungsi, dua atau lebih ruang lingkup sewenang-wenang jelas merupakan indikasi yang baik bahwa Anda perlu refactor. Namun, itu muncul sebagai solusi yang waras dalam hal-hal seperti beralih blok kasus dari waktu ke waktu.
tgl
16

Karena di masa lalu C variabel baru hanya bisa dideklarasikan di blok baru.

Dengan cara ini programmer dapat memperkenalkan variabel baru di tengah fungsi tanpa membocorkannya dan meminimalkan penggunaan stack.

Dengan pengoptimal hari ini tidak ada gunanya dan tanda bahwa Anda perlu mempertimbangkan mengekstraksi blok dalam fungsinya sendiri.

Dalam pernyataan beralih, penting untuk melampirkan kasus di blok mereka sendiri untuk menghindari deklarasi ganda.

Dalam C ++ ini sangat berguna misalnya untuk penjaga kunci RAII dan memastikan destruktor menjalankan kunci pelepasan ketika eksekusi keluar dari ruang lingkup dan masih melakukan hal-hal lain di luar bagian kritis.

orang aneh
sumber
2
+1 untuk penjaga kunci RAII. Yang sedang berkata, tidak bisakah konsep yang sama ini juga berguna untuk deallokasi buffer buffer besar dalam bagian rutin? Saya belum pernah melakukannya, tetapi terdengar seperti sesuatu yang pasti dapat terjadi pada beberapa kode yang disematkan ...
J Trana
2

Saya tidak akan melihatnya sebagai blok "sewenang-wenang". Ini bukan fitur yang sangat berarti bagi penggunaan pengembang, tetapi cara C menggunakan blok memungkinkan konstruksi blok yang sama untuk digunakan di sejumlah tempat dengan semantik yang sama. Blok (dalam C) adalah ruang lingkup baru, dan variabel yang membiarkannya dihilangkan. Ini seragam terlepas dari bagaimana blok digunakan.

Dalam bahasa lain, ini bukan masalahnya. Ini memiliki keuntungan dengan memungkinkan lebih sedikit penyalahgunaan seperti yang Anda tunjukkan, tetapi kerugian yang menghalangi perilaku berbeda tergantung pada apa konteksnya.

Saya jarang melihat blok mandiri yang digunakan dalam C atau C ++ - sering kali ketika ada struktur besar atau objek yang mewakili koneksi atau sesuatu yang Anda ingin paksa penghancurannya. Biasanya ini adalah petunjuk bahwa fungsi Anda melakukan terlalu banyak hal dan / atau terlalu lama.

Telastyn
sumber
1
Sayangnya saya secara teratur melihat blok yang berdiri sendiri - dalam hampir semua kasus karena fungsinya terlalu banyak dan / atau terlalu lama.
mattnz
Saya juga secara teratur melihat dan menulis blok mandiri di C ++ tetapi hanya untuk memaksa penghancuran pembungkus di sekitar kunci dan sumber daya bersama lainnya.
J Trana
2

Anda harus sadar, prinsip-prinsip pemrograman yang tampak jelas sekarang tidak selalu demikian. Praktik terbaik C sangat tergantung pada berapa usia contoh Anda. Ketika C pertama kali diperkenalkan, memecah kode Anda menjadi fungsi-fungsi kecil dianggap terlalu tidak efisien. Dennis Ritchie pada dasarnya berbohong dan mengatakan panggilan fungsi sangat efisien dalam C (mereka tidak pada saat itu), yang membuat orang mulai menggunakan mereka lebih banyak, meskipun programmer C entah bagaimana tidak pernah benar-benar melewati budaya optimalisasi prematur.

Ini adalah praktik pemrograman yang baik bahkan hari ini untuk membatasi ruang lingkup variabel Anda sekecil mungkin. Saat ini, kami biasanya melakukan itu dengan membuat fungsi baru, tetapi jika fungsi dianggap mahal, memperkenalkan blok baru adalah cara logis untuk membatasi ruang lingkup Anda tanpa overhead panggilan fungsi.

Namun, saya mulai pemrograman dalam C lebih dari 20 tahun yang lalu, ketika Anda harus mendeklarasikan semua variabel Anda di bagian atas ruang lingkup, dan saya tidak ingat variabel shadowing seperti itu pernah dianggap gaya yang baik. Mendeklar ulang dalam dua blok satu demi satu seperti dalam pernyataan switch, ya, tapi tidak membayangi. Mungkin jika variabel sudah digunakan dan nama spesifik adalah sangat idiomatic untuk API Anda menelepon, seperti destdan srcdi strcpy, misalnya.

Karl Bielefeldt
sumber
1

Blok arbitrer berguna untuk memperkenalkan variabel perantara yang hanya digunakan dalam kasus khusus perhitungan.

Ini adalah pola umum dalam komputasi ilmiah, di mana prosedur numerik biasanya:

  1. mengandalkan banyak parameter atau jumlah perantara;
  2. harus berurusan dengan banyak kasus khusus.

Karena poin kedua, berguna untuk memperkenalkan variabel sementara lingkup terbatas, yang dicapai jika menggunakan blok arbitrer atau dengan memperkenalkan fungsi bantu.

Sementara memperkenalkan fungsi tambahan mungkin terlihat seperti tidak punya otak atau praktik terbaik untuk diikuti secara membabi buta, sebenarnya ada sedikit manfaat untuk melakukannya dalam situasi khusus ini.

Karena ada banyak parameter dan jumlah perantara, kami ingin memperkenalkan struktur untuk meneruskannya ke fungsi bantu.

Tetapi, karena kami ingin menjadi konsekuen dengan praktik kami, kami tidak akan memperkenalkan hanya satu fungsi tambahan tetapi beberapa. Jadi, apakah kita memperkenalkan struktur ad-hoc menyampaikan parameter untuk setiap fungsi, yang memperkenalkan banyak kode-overhead untuk memindahkan parameter bolak-balik, atau kami memperkenalkan satu akan memerintah mereka semua struktur lembar kerja, yang berisi semua variabel kami tetapi terlihat seperti grabpack bit tanpa konsistensi, di mana setiap saat hanya separuh parameter memiliki makna yang menarik.

Oleh karena itu struktur bantu ini biasanya rumit dan menggunakannya berarti memilih antara kode-mengasapi atau memperkenalkan abstraksi yang ruang lingkupnya terlalu luas dan melemahkan arti program, alih-alih memilahnya .

Memperkenalkan fungsi bantu dapat memudahkan pengujian unit pada program dengan memperkenalkan granularitas uji yang lebih baik tetapi menggabungkan pengujian unit untuk tidak bahwa prosedur tingkat rendah dan pengujian regresi dalam bentuk perbandingan (dengan jumlah tambahan) jejak numerik prosedur melakukan pekerjaan yang sama baiknya .

pengguna40989
sumber
0

Secara umum, penggunaan kembali nama variabel dengan cara ini menyebabkan terlalu banyak kebingungan bagi pembaca kode Anda di masa mendatang. Lebih baik hanya memberi nama variabel dalam sesuatu yang lain. Bahkan, bahasa C # secara khusus melarang penggunaan variabel dengan cara ini.

Saya bisa melihat bagaimana penggunaan blok bersarang dalam ekspansi makro akan berguna untuk mencegah tabrakan penamaan variabel.

Robert Harvey
sumber
0

Ini untuk pelingkupan hal-hal yang biasanya tidak memiliki cakupan pada tingkat itu. Ini sangat jarang, dan secara umum refactoring adalah pilihan yang lebih baik, tapi saya pernah bertemu sekali atau dua kali di masa lalu dalam pernyataan switch:

switch(foo) {
   case 1:
      {
         // bar
      }
   case 2:
   case 3:
      // baz
      break;
   case 4:
   case 5:
      // bang
      break;
}

Ketika Anda mempertimbangkan pemeliharaan, refactoring ini harus seimbang dengan memiliki semua implementasi berturut-turut, selama masing-masing hanya beberapa baris panjang. Lebih mudah untuk menggabungkan semuanya, daripada daftar nama fungsi.

Dalam kasus saya, jika saya ingat dengan benar, sebagian besar kasus adalah sedikit variasi dari kode yang sama - kecuali bahwa salah satu dari mereka identik dengan yang lain, hanya dengan preprocessing tambahan. Switch akhirnya menjadi bentuk paling sederhana untuk mengambil kode, dan ruang lingkup tambahan memungkinkan penggunaan variabel lokal ekstra yang tidak perlu dikhawatirkan olehnya (seperti nama variabel yang secara tidak sengaja tumpang tindih dengan yang didefinisikan sebelum switch tetapi digunakan nanti).

Perhatikan bahwa pernyataan peralihan hanyalah penggunaannya yang pernah saya jumpai. Saya yakin itu bisa diterapkan di tempat lain juga, tetapi saya tidak ingat melihat mereka digunakan secara efektif seperti itu.

Izkata
sumber