Dalam C, apakah tanda kurung bertindak seperti tumpukan tumpukan?

Jika saya membuat variabel dalam set kurung keriting yang baru, apakah variabel ini terlepas dari tumpukan pada braket penutup, atau apakah itu menggantung hingga akhir fungsi? Sebagai contoh:

 void foo() { int c[100]; { int d[200]; } //code that takes a while return; } 

Akankah mengambil memori dalam code that takes a while bagian code that takes a while ?

148
03 мая '10 в 19:02 2010-05-03 19:02 Claudiu diatur untuk 03 Mei 10 di 19:02 2010-05-03 19:02
@ 8 jawaban

Tidak, kawat gigi tidak bertindak sebagai tumpukan tumpukan. Dalam C, kurung keriting hanya menunjukkan namespace, tetapi tidak ada yang hancur dan tidak ada yang didorong keluar dari tumpukan ketika kontrol meninggalkannya.

Sebagai seorang programmer yang menulis kode, Anda sering dapat menganggapnya sebagai tumpukan stack. Pengidentifikasi yang dideklarasikan dalam kurung keriting hanya tersedia dalam kurung keriting, jadi dari sudut pandang programmer, ini terlihat seperti mereka didorong ke tumpukan seperti yang dideklarasikan, dan kemudian didorong keluar dari wilayah tersebut. Namun, kompiler tidak perlu membuat kode yang mendorong / mendorong sesuatu pada input / output (dan biasanya ini tidak terjadi).

Perhatikan juga bahwa variabel lokal mungkin tidak menggunakan ruang stack sama sekali: mereka dapat disimpan dalam register CPU atau di beberapa penyimpanan tambahan lainnya atau dioptimalkan sepenuhnya.

Jadi, array d , secara teoritis, dapat mengkonsumsi memori untuk seluruh fungsi. Namun, kompiler dapat mengoptimalkannya atau berbagi memorinya dengan variabel lokal lain yang masa pakainya tidak tumpang tindih.

76
03 мая '10 в 19:03 2010-05-03 19:03 membalas Kristopher Johnson pada 03 Mei '10 pukul 19:03 2010-05-03 19:03

Waktu di mana variabel benar-benar memakan memori jelas tergantung pada kompiler (dan banyak kompiler tidak mengatur stack pointer ketika blok internal dimasukkan dan keluar fungsi).

Namun, pertanyaan yang berkaitan erat, tetapi mungkin lebih menarik, adalah apakah program diizinkan untuk mengakses objek internal ini di luar area internal (tetapi di dalam fungsi yang terkandung), yaitu:

 void foo() { int c[100]; int *p; { int d[200]; p = d; }  return; } 

(Dengan kata lain: apakah kompiler memperbolehkan d dirilis, walaupun dalam prakteknya sebagian besar tidak berfungsi?).

Jawabannya adalah ya, kompiler diperbolehkan untuk bebas d , dan akses ke p[0] , di mana komentar menunjukkan, adalah perilaku yang tidak ditentukan. Bagian yang relevan dari standar C adalah 6.2.4p5:

Untuk objek semacam itu [yang memiliki waktu penyimpanan otomatis], yang tidak memiliki tipe array yang panjang variabel, masa pakainya ber> . (Memasuki blok tertutup atau memanggil fungsi berhenti, tetapi tidak berakhir, eksekusi blok saat ini.) Jika blok dimasukkan secara rekursif, instance objek baru dibuat setiap kali. Nilai awal objek tidak terdefinisi. Jika inisialisasi adalah untuk suatu objek, itu dilakukan setiap kali deklarasi tercapai ketika blok dijalankan; jika tidak, nilai menjadi tidak terdefinisi setiap kali deklarasi tercapai.

33
04 мая '10 в 4:15 2010-05-04 04:15 jawaban yang diberikan caf 04 Mei '10 pada 4:15 2010-05-04 04:15

Pertanyaan Anda tidak cukup jelas untuk menjawab dengan tegas.

Di satu sisi, kompiler biasanya tidak melakukan alokasi bebas-memori lokal untuk blok bersarang. Memori lokal biasanya ditugaskan hanya sekali ketika suatu fungsi dimasukkan dan dilepaskan ketika fungsi keluar.

Di sisi lain, ketika masa hidup objek lokal berakhir, memori yang ditempati oleh objek ini dapat digunakan kembali untuk objek lokal lain nanti. Misalnya, dalam kode ini

 void foo() { { int d[100]; } { double e[20]; } } 

Kedua array biasanya menempati area memori yang sama, yang berarti bahwa jumlah total penyimpanan lokal yang diperlukan oleh fungsi foo adalah apa yang diperlukan untuk yang terbesar dari dua array, dan bukan untuk keduanya pada saat yang bersamaan.

Bisakah yang terakhir memenuhi syarat sebagai d , terus menduduki memori sampai akhir fungsi dalam konteks pertanyaan Anda, untuk Anda putuskan.

17
03 мая '10 в 20:14 2010-05-03 20:14 Balasan diberikan oleh AnT 03 Mei, '10 pada 20:14 2010-05-03 20:14

Itu tergantung implementasinya. Saya menulis sebuah program pendek untuk memeriksa apa yang dilakukan gcc 4.3.4, dan pada saat yang sama mengalokasikan seluruh ruang stack pada awal fungsi. Anda dapat memeriksa build yang dihasilkan gcc menggunakan flag -S.

6
03 мая '10 в 19:40 2010-05-03 19:40 membalas Daniel Stutzbach pada 03 Mei 10 jam 19:40 2010-05-03 19:40

Tidak, d [] tidak akan berada di tumpukan untuk sisa subrutin. Tetapi pengalokasian () berbeda.

Sunting: Christopher Johnson (dan Simon dan Daniel) benar, dan jawaban awal saya salah . Dengan gcc 4.3.4.pada kode CYGWIN:

 void foo(int[]); void bar(void); void foobar(int); void foobar(int flag) { if (flag) { int big[100000000]; foo(big); } bar(); } 

memberi:

 _foobar: pushl %ebp movl %esp, %ebp movl $400000008, %eax call __alloca cmpl $0, 8(%ebp) je L2 leal -400000000(%ebp), %eax movl %eax, (%esp) call _foo L2: call _bar leave ret 

Hidup dan belajar! Dan tes cepat tampaknya menunjukkan bahwa AndreyT juga benar untuk banyak distribusi.

Ditambahkan jauh kemudian . Tes di atas menunjukkan bahwa dokumentasi gcc tidak tepat. Selama bertahun-tahun, katanya (disoroti oleh saya):

"Ruang untuk array panjang variabel dibebaskan segera setelah area nama array selesai ."

3
03 мая '10 в 20:06 2010-05-03 20:06 Jawaban diberikan oleh Joseph Quinsey pada 03 Mei 10 di 20:06 2010-05-03 20:06

Variabel d Anda biasanya tidak dihapus dari tumpukan. Kurung keriting tidak menunjukkan tumpukan. Jika tidak, Anda tidak akan dapat melakukan hal seperti ini:

 char var = getch(); { char next_var = var + 1; use_variable(next_char); } 

Jika kawat gigi disebut push / pop stack yang sebenarnya (misalnya, panggilan fungsi), maka kode di atas tidak akan dikompilasi karena kode di dalam kawat gigi tidak akan dapat mengakses variabel var yang tinggal di luar kawat gigi (seperti halnya subfungsi tidak dapat secara >

Kawat gigi hanya digunakan dalam kawat gigi. Kompiler akan menangani akses apa pun ke variabel "internal" dari luar tanda kurung tutup sebagai tidak valid, dan dapat menggunakan kembali memori ini untuk sesuatu yang lain (ini tergantung pada implementasinya). Namun, itu tidak dapat dihapus dari tumpukan sampai fungsi tutup kembali.

Pembaruan: Inilah yang seharusnya dikatakan oleh C spec . Mengenai benda dengan waktu penyimpanan otomatis (bagian 6.4.2):

Untuk objek yang tidak memiliki tipe array panjang variabel, masa pakainya memanjang dari input ke blok yang dikaitkan dengannya hingga eksekusi blok ini berakhir.

Di bagian yang sama, istilah "seumur hidup" didefinisikan sebagai (fokus utama):

Seumur hidup objek adalah bagian dari eksekusi program selama penyimpanan dicadangkan. . Objek itu ada, memiliki alamat permanen dan mempertahankan nilai terakhir yang disimpan selama masa pakainya. Jika objek tersebut disebutkan di luar masa pakainya, perilaku tidak terdefinisi.

Kata kuncinya di sini adalah, tentu saja, "dijamin." Ketika Anda meninggalkan area set dalam kurung kurawal, umur array sudah selesai. Penyimpanan mungkin atau mungkin tidak dialokasikan untuk itu (kompiler Anda dapat menggunakan kembali ruang untuk sesuatu yang lain), tetapi setiap upaya untuk mengakses array menyebabkan perilaku yang tidak ditentukan dan menyebabkan hasil yang tidak terduga.

Tidak ada konsep stack frame dalam spesifikasi C. Dia hanya berbicara tentang bagaimana program yang dihasilkan akan dipertahankan dan meninggalkan detail implementasi ke kompiler (setelah semua, implementasi akan lebih mirip prosesor gratis daripada pada prosesor dengan tumpukan perangkat keras). Tidak ada dalam specifier C yang menunjukkan di mana frame stack akan atau tidak akan berakhir. Satu-satunya cara nyata untuk mengetahuinya adalah dengan mengkompilasi kode pada kompiler / platform spesifik Anda dan memeriksa hasil build. Opsi optimasi Anda saat ini untuk kompiler Anda juga kemungkinan akan memainkan peran dalam hal ini.

Jika Anda ingin array d tidak lagi menggunakan memori saat kode Anda sedang berjalan, Anda dapat mengubah kode menjadi kawat gigi menjadi fungsi terpisah, atau secara eksplisit malloc dan free memori daripada menggunakan penyimpanan otomatis.

1
03 мая '10 в 20:59 2010-05-03 20:59 Jawabannya diberikan bta 03 Mei '10 pada 20:59 2010-05-03 20:59

Mereka bisa. Mereka tidak bisa. Jawaban yang menurut saya sangat Anda butuhkan: Jangan pernah berasumsi apa pun. Kompiler modern membuat semua jenis arsitektur dan sihir khusus untuk implementasi. Tulis kode Anda secara sederhana dan jelas untuk orang-orang dan biarkan kompiler melakukan hal yang baik. Jika Anda mencoba membuat kode di sekitar kompiler, Anda meminta masalah - dan masalah yang biasanya Anda dapatkan dalam situasi ini biasanya sangat halus dan sulit untuk didiagnosis.

1
03 мая '10 в 23:05 2010-05-03 23:05 jawabannya diberikan oleh user19666 03 Mei '10 pada 23:05 2010-05-03 23:05

Saya percaya bahwa itu keluar dari ruang lingkup, tetapi tidak dihapus dari tumpukan sampai fungsi kembali. Dengan demikian, masih akan menempati memori di stack sampai fungsi selesai, tetapi tidak tersedia setelah kurung tutup pertama.

0
03 мая '10 в 19:07 2010-05-03 19:07 Balasan diberikan oleh simon 03 Mei '10 pada 19:07 2010-05-03 19:07

Pertanyaan lain pada tag atau Ajukan pertanyaan