Bagaimana mungkin untuk mendeklarasikan tidak ada di dalam main () di C ++ namun memiliki aplikasi yang berfungsi setelah kompilasi?

86

Dalam sebuah wawancara saya dihadapkan pada pertanyaan seperti ini:

Teman Anda telah memberi Anda satu file kode sumber yang mencetak angka Fibonacci di konsol. Perhatikan bahwa blok main () kosong dan tidak memiliki pernyataan apa pun di dalamnya.

Jelaskan bagaimana ini mungkin (petunjuk: contoh global!)

Saya benar-benar ingin tahu tentang ini, bagaimana hal seperti itu bisa menjadi mungkin!

Rika
sumber
26
Lihat petunjuknya!
R. Martinho Fernandes
14
Karena itu adalah sesuatu yang 1) saya belum pernah dengar, 2) hal sepele yang berguna karena orang-orang menanyakannya dalam wawancara, 3) aplikasi bahasa yang menarik untuk diketahui sehingga 4) saya dapat mengenalinya dan menusuk wajah siapa pun dengan belati berkarat jika saya melihat mereka benar-benar menggunakannya dalam kode produksi.
OmnipotentEntity
4
Seorang programmer C ++ yang kompeten dan profesional akan mengetahui jawaban dari pertanyaan ini. Jika tujuan pertanyaan wawancara ini adalah untuk menentukan apakah orang yang diwawancarai adalah programmer C ++ profesional yang kompeten, pertanyaan tersebut tidak boleh memberi mereka jawaban.
John Dibling
1
Dalam pengaturan wawancara, salah satu alternatifnya adalah memiliki logika di dalam fungsi apa pun dalam kode dan mencatat keluaran menggunakan assertatau #pragma messagedll. Ini akan mengarahkan keluaran ke konsol selama kompilasi. Program ini mungkin tidak pernah sepenuhnya dikompilasi, tetapi ini pasti cara yang menyenangkan untuk menunjukkan pemikiran "out-of-the-box" Anda selama wawancara. Ini memenuhi pertanyaan yang dikutip karena TIDAK menyebutkan apa pun tentang biner yang dihasilkan; melainkan hanya berbicara tentang file C yang dapat menampilkan "barang" di konsol. ;-)
TheCodeArtist
1
Apakah ini wawancara untuk IOCC ? :-) Ok, saya akui saya sering melakukannya untuk menginisialisasi pabrik saya atau menjalankan beberapa kode uji. Btw, ' file kode sumber tunggal ' juga merupakan petunjuk, bahwa entri-pint (utama secara default) tidak diganti oleh linker.
Valentin Heinitz

Jawaban:

127

Ini kemungkinan besar diterapkan sebagai (atau varian dari itu):

 void print_fibs() 
 {
       //implementation
 }

 int ignore = (print_fibs(), 0);

 int main() {}

Dalam kode ini, variabel global ignoreharus diinisialisasi sebelum masuk ke main()fungsi. Sekarang untuk menginisialisasi global, print_fibs()perlu dijalankan di mana Anda dapat melakukan apa saja - dalam hal ini, hitung nomor fibonacci dan cetak! Hal serupa yang saya tunjukkan dalam pertanyaan berikut (yang sudah lama saya tanyakan):

Perhatikan bahwa kode seperti itu tidak aman dan sebaiknya dihindari secara umum. Misalnya, std::coutobjek mungkin tidak diinisialisasi saat print_fibs()dijalankan, jika demikian maka apa yang akan std::coutdilakukan dalam fungsinya? Namun, jika dalam keadaan lain, tidak bergantung pada urutan inisialisasi, maka aman untuk memanggil fungsi inisialisasi (yang merupakan praktik umum di C dan C ++).

Nawaz
sumber
3
@Nawaz Mungkin layak mengutip jaminan yang tepat. Objek dalam unit terjemahan dijamin akan diinisialisasi secara berurutan. Objek aliran standar dijamin akan diinisialisasi sebelum atau selama inisialisasi pertama std::ios_base::Initobjek. Dan <iostream>dijamin untuk berperilaku "seolah-olah" itu berisi instance dari std::ios_base_Initobjek pada lingkup namespace.
James Kanze
3
@ Steve314: Itu tidak mengembalikan apa pun, itulah sebabnya saya menggunakan operator koma, untuk memastikan bahwa jenis ekspresi keseluruhan (print_fibs(), 0)adalah int. Ini Demo Online .
Nawaz
1
@Nawaz Alternatif untuk fungsi void dan operator koma adalah mengembalikan a bool, dan variabel bool fibsPrinted. Itu mungkin sedikit lebih bersih jika fungsinya hanya berfungsi di sini. (Tapi perbedaannya mungkin tidak cukup untuk dikhawatirkan.)
James Kanze
1
+1, Bicara tentang keren. Harus bergabung dengan stackoverflow hanya untuk memberi suara positif pada pertanyaan ini dan jawaban ini.
Titik Tetap
1
@Nawaz Saya tidak yakin apa maksud Anda. Definisi std::coutada di suatu tempat di perpustakaan. Tapi seperti yang telah saya tunjukkan, standar mengharuskannya untuk diinisialisasi sebelum konstruktor pertama dari suatu std::ios_base::Initobjek selesai, dan itu mengharuskan termasuk <iostream>berperilaku seolah-olah sebuah std::ios_base::Initobjek didefinisikan pada ruang lingkup namespace. Jika unit terjemahan termasuk <iostream>sebelum definisi objek yang diinisialisasi, std::coutdijamin akan dibangun.
James Kanze
18

Semoga ini membantu

class cls
{
  public:
    cls()
    {
      // Your code for fibonacci series
    }
} objCls;

int main()
{
}

Jadi segera setelah variabel global kelas dideklarasikan, konstruktor dipanggil dan di sana Anda menambahkan logika untuk mencetak deret Fibonacci.

Saksham
sumber
9

Ya itu mungkin. Anda perlu mendeklarasikan instance global dari suatu objek yang menghitung bilangan Fibonacci dalam konstruktor objek.

Tn. Beer
sumber
6
Anda perlu mendeklarasikan instance global dari sebuah objek yang penginisialisasinya menghitung angka Fibonacci.
James Kanze
4

Saya tahu beberapa contoh seperti yang Anda ceritakan. Salah satu cara mendapatkannya adalah dengan menggunakan template metaprogramming. Dengan menggunakannya, Anda dapat memindahkan beberapa proses komputasi ke kompilasi.

Di sini Anda bisa mendapatkan contoh dengan angka Fibonacci

Jika Anda menggunakannya dalam konstruktor kelas statis dan Anda dapat menulis angka tanpa perlu menulis kode apa pun di fungsi utama.

Semoga membantu Anda.

superarce
sumber
3

Hal-hal dapat terjadi selama inisialisasi variabel global / statis. Kode akan dipicu saat aplikasi dimulai.

log0
sumber
3

Semua konstruktor [*] untuk objek cakupan file dipanggil sebelum mencapai main, seperti halnya semua ekspresi penginisialisasi untuk variabel cakupan file non-objek.

Sunting: Juga, semua penghancur [*] untuk semua objek lingkup file dipanggil dalam urutan terbalik konstruksi setelah mainkeluar. Anda bisa, secara teoritis, meletakkan program fibonacci Anda pada sebuah penghancur benda.

[*] Perhatikan bahwa 'semua' mengabaikan perilaku pemuatan dan pembongkaran pustaka secara dinamis yang tidak ditautkan secara langsung oleh program Anda. Mereka secara teknis berada di luar bahasa C ++ dasar.

Joe Z
sumber
Semuanya ? Bahkan orang-orang di dll yang secara eksplisit dimuat setelah main?
James Kanze
Nah, C ++ tidak secara teknis mendefinisikan pustaka yang dimuat secara dinamis, jadi dalam C ++ murni, pernyataan saya benar. Jadi, buat bayangan "Semua, simpan untuk penginisialisasi dan objek cakupan file yang terdapat dalam DLL / DSO yang dimuat setelah mencapai main." Dalam hal ini, mainkosong, jadi DLL / DSO tersebut harus dimuat oleh destruktor, yang sangat menyimpang. Tapi, karena ilmu komputer, saya kira kita harus berhati-hati dengan kata-kata seperti "semua".
Joe Z
Saya menambahkan peringatan 'semua' ke jawaban saya di atas, dan juga menambahkan catatan tentang dtors.
Joe Z
Ya, tapi semoga itu akan datang. Pre C ++ 11 berisi beberapa kata-kata musang yang dimaksudkan untuk mengizinkan DLL, tetapi yang dalam praktiknya hanya berarti bahwa secara teknis, jaminan tidak selalu ada, meskipun itu ada di semua implementasi aktual, dan banyak kode bergantung padanya. C ++ 11 memperbaikinya, setidaknya.
James Kanze