Apakah main () benar-benar memulai program C ++?

131

Bagian $ 3.6.1 / 1 dari C ++ Standard berbunyi,

Suatu program harus mengandung fungsi global yang disebut main , yang merupakan awal dari program yang ditunjuk .

Sekarang pertimbangkan kode ini,

int square(int i) { return i*i; }
int user_main()
{ 
    for ( int i = 0 ; i < 10 ; ++i )
           std::cout << square(i) << endl;
    return 0;
}
int main_ret= user_main();
int main() 
{
        return main_ret;
}

Kode contoh ini melakukan apa yang ingin saya lakukan, yaitu mencetak kuadrat dari 0 hingga 9, sebelum masuk ke main()fungsi yang seharusnya menjadi "awal" dari program.

Saya juga mengkompilasinya dengan -pedanticopsi, GCC 4.5.0. Tidak ada kesalahan, bahkan peringatan!

Jadi pertanyaan saya adalah,

Apakah kode ini benar-benar sesuai standar?

Jika itu sesuai standar, maka apakah itu tidak membatalkan apa yang dikatakan Standar? main()belum memulai program ini! user_main()dieksekusi sebelum main().

Saya mengerti bahwa untuk menginisialisasi variabel global main_ret, use_main()pertama-tama dijalankan tetapi itu adalah hal yang berbeda sama sekali; intinya adalah, itu tidak valid pernyataan dikutip $ 3.6.1 / 1 dari Standar, karena main()BUKAN awal program; ini sebenarnya adalah akhir dari program ini !


EDIT:

Bagaimana Anda mendefinisikan kata 'mulai'?

Itu bermuara pada definisi frase "mulai dari program" . Jadi, bagaimana tepatnya Anda mendefinisikannya?

Nawaz
sumber

Jawaban:

85

Tidak, C ++ melakukan banyak hal untuk "mengatur lingkungan" sebelum panggilan utama; namun, utama adalah awal resmi bagian "yang ditentukan pengguna" dari program C ++.

Beberapa pengaturan lingkungan tidak dapat dikontrol (seperti kode awal untuk mengatur std :: cout; namun, beberapa lingkungan dapat dikontrol seperti blok global statis (untuk menginisialisasi variabel global statis). Perhatikan bahwa karena Anda tidak memiliki penuh kontrol sebelum main, Anda tidak memiliki kontrol penuh pada urutan blok statis diinisialisasi.

Setelah main, kode Anda secara konseptual "sepenuhnya mengendalikan" program, dalam arti bahwa Anda berdua dapat menentukan instruksi yang harus dilakukan dan urutan pelaksanaannya. Multi-threading dapat mengatur ulang urutan eksekusi kode; tetapi, Anda masih memegang kendali dengan C ++ karena Anda menetapkan agar bagian-bagian kode dieksekusi (mungkin) tidak sesuai pesanan.

Edwin Buck
sumber
9
+1 untuk ini "Perhatikan bahwa karena Anda tidak memiliki kontrol penuh sebelum main, Anda tidak memiliki kontrol penuh pada urutan di mana blok statis diinisialisasi. Setelah main, kode Anda secara konseptual" sepenuhnya mengendalikan "dari program, dalam arti bahwa Anda berdua dapat menentukan instruksi yang akan dilakukan dan urutan yang harus dilakukan " . Ini juga membuat saya menandai jawaban ini sebagai jawaban yang diterima ... Saya pikir ini adalah poin yang sangat penting, yang cukup dibenarkan main()sebagai "awal program"
Nawaz
13
@Nawaz: perhatikan bahwa di atas tidak ada kontrol penuh atas urutan inisialisasi, Anda tidak memiliki kontrol atas kesalahan inisialisasi: Anda tidak dapat menangkap pengecualian pada lingkup global.
André Caron
@Nawaz: Apa itu blok global statis? tolong jelaskan dengan contoh sederhana? Terima kasih
Destructor
@meet: Objek yang dideklarasikan di level namespace memiliki staticdurasi penyimpanan, dan dengan demikian, objek-objek ini milik unit terjemahan yang berbeda dapat diinisialisasi dalam urutan apa pun (karena urutan tidak ditentukan oleh standar). Saya tidak yakin apakah itu menjawab pertanyaan Anda, meskipun itu yang bisa saya katakan dalam konteks topik ini.
Nawaz
88

Anda salah membaca kalimat.

Suatu program harus mengandung fungsi global yang disebut main, yang merupakan awal dari program yang ditunjuk.

Standar ini mendefinisikan kata "mulai" untuk keperluan sisa standar. Itu tidak mengatakan bahwa tidak ada kode yang dijalankan sebelum maindipanggil. Dikatakan bahwa awal program dianggap berada pada fungsinya main.

Program Anda patuh. Program Anda belum "mulai" sampai main dimulai. Konstruktor dipanggil sebelum program Anda "dimulai" sesuai dengan definisi "mulai" dalam standar, tetapi itu tidak terlalu penting. BANYAK kode dijalankan sebelum mainini pernah disebut dalam setiap program, tidak hanya contoh ini.

Untuk keperluan diskusi, kode konstruktor Anda dijalankan sebelum 'mulai' program, dan yang sepenuhnya sesuai dengan standar.

Adam Davis
sumber
3
Maaf, tapi saya tidak setuju dengan interpretasi Anda tentang klausa itu.
Lightness Races in Orbit
Saya pikir Adam Davis benar, "main" lebih seperti semacam pembatasan pengkodean.
laike9m
@LightnessRacesinOrbit Saya tidak pernah melakukan tindak lanjut, tetapi bagi saya kalimat itu dapat diringkas secara logis menjadi "fungsi global yang disebut main adalah awal yang ditunjuk dari program" (penekanan ditambahkan). Apa interpretasi Anda terhadap kalimat itu?
Adam Davis
1
@AdamDavis: Saya tidak ingat apa yang menjadi perhatian saya. Saya tidak bisa memikirkannya sekarang.
Lightness Races in Orbit
23

Program Anda tidak akan ditautkan dan karenanya tidak akan berjalan kecuali ada yang utama. Namun main () tidak menyebabkan dimulainya eksekusi program karena objek pada level file memiliki konstruktor yang berjalan sebelumnya dan akan mungkin untuk menulis seluruh program yang menjalankan masa hidupnya sebelum main () tercapai dan membiarkan main itu sendiri memiliki tubuh kosong.

Pada kenyataannya untuk menegakkan ini, Anda harus memiliki satu objek yang dibangun sebelum utama dan konstruktornya untuk memanggil semua aliran program.

Lihat ini:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

Aliran program Anda akan secara efektif berasal Foo::Foo()

Uang tunai
sumber
13
+1. Tetapi perhatikan bahwa jika Anda memiliki banyak objek global dalam unit terjemahan yang berbeda, ini akan membuat Anda dalam kesulitan dengan cepat karena urutan penggunaan konstruktor tidak terdefinisi. Anda dapat pergi dengan lajang dan inisialisasi malas, tetapi dalam lingkungan multithreaded, semuanya menjadi sangat buruk dengan cepat. Dalam satu kata, jangan lakukan ini dalam kode nyata.
Alexandre C.
3
Sementara Anda mungkin harus memberikan main () tubuh yang tepat dalam kode Anda dan memungkinkannya untuk menjalankan eksekusi, konsep objek di luar yang memulai adalah apa yang banyak didasarkan pada pustaka LD_PRELOAD.
CashCow
2
@Alex: Standar ini mengatakan tidak terdefinisi, tetapi sebagai urutan tautan praktis (biasanya, tergantung pada kompiler) menentang urutan inisialisasi.
ThomasMcLeod
1
@ Thomas: Saya pasti tidak akan mencoba untuk mengandalkan itu dari jauh. Saya juga pasti tidak akan mencoba mengendalikan sistem build secara manual.
Alexandre C.
1
@Alex: tidak begitu penting lagi, tetapi kembali pada hari kita akan menggunakan urutan tautan untuk mengontrol build image sehingga mengurangi paging memori fisik. Ada alasan sisi lain Anda mungkin ingin mengontrol urutan initisasi bahkan ketika itu tidak mempengaruhi semantik program, seperti pengujian perbandingan kinerja startup.
ThomasMcLeod
15

Anda menandai pertanyaan sebagai "C" juga, kemudian, berbicara tentang C, inisialisasi Anda akan gagal sesuai bagian 6.7.8 "Inisialisasi" dari standar ISO C99.

Yang paling relevan dalam kasus ini tampaknya menjadi kendala # 4 yang mengatakan:

Semua ekspresi dalam penginisialisasi untuk objek yang memiliki durasi penyimpanan statis harus berupa ekspresi konstan atau string literal.

Jadi, jawaban untuk pertanyaan Anda adalah bahwa kode tidak sesuai dengan standar C.

Anda mungkin ingin menghapus tag "C" jika Anda hanya tertarik dengan standar C ++.

Remo.D
sumber
4
@ Remo.D dapatkah Anda memberi tahu kami apa yang ada di bagian itu. Tidak semua dari kita memiliki standar C :).
UmmaGumma
2
Karena Anda sangat pemilih: Alas, ANSI C telah usang sejak 1989. ISO C90 atau C99 adalah standar yang relevan untuk dikutip.
Lundin
@Lundin: Tidak ada yang cukup pilih-pilih :) Saya membaca ISO C99 tapi saya cukup yakin itu berlaku untuk C90 juga.
Remo.D
@Ashot. Anda benar, menambahkan kalimat yang saya pikir paling relevan di sini.
Remo.D
3
@Remo: +1 untuk memberikan info bahwa itu tidak valid C; saya tidak tahu itu. Lihat ini bagaimana orang belajar, kadang-kadang dengan rencana, kadang-kadang secara kebetulan!
Nawaz
10

Bagian 3.6 secara keseluruhan sangat jelas tentang interaksi maindan inisialisasi dinamis. "Awal yang ditentukan dari program" tidak digunakan di tempat lain dan hanya deskriptif dari maksud umum main(). Tidak masuk akal untuk menafsirkan satu frasa dengan cara normatif yang bertentangan dengan persyaratan yang lebih rinci dan jelas dalam Standar.

aschepler
sumber
9

Compiler sering harus menambahkan kode sebelum main () untuk menjadi standar compliant. Karena standar menetapkan bahwa inisialisasi global / statika harus dilakukan sebelum program dijalankan. Dan seperti yang disebutkan, hal yang sama berlaku untuk konstruktor objek yang ditempatkan pada ruang lingkup file (global).

Dengan demikian pertanyaan awal juga relevan untuk C, karena dalam program C Anda masih memiliki inisialisasi global / statis yang harus dilakukan sebelum program dapat dimulai.

Standar mengasumsikan bahwa variabel-variabel ini diinisialisasi melalui "sihir", karena mereka tidak mengatakan bagaimana mereka harus ditetapkan sebelum inisialisasi program. Saya pikir mereka menganggap itu sebagai sesuatu di luar ruang lingkup standar bahasa pemrograman.

Sunting: Lihat misalnya ISO 9899: 1999 5.1.2:

Semua objek dengan durasi penyimpanan statis harus diinisialisasi (diatur ke nilai awal) sebelum memulai program. Cara dan waktu inisialisasi tersebut tidak ditentukan.

Teori di balik bagaimana "sihir" ini harus dilakukan kembali ke kelahiran C, ketika itu adalah bahasa pemrograman yang dimaksudkan untuk digunakan hanya untuk OS UNIX, pada komputer berbasis RAM. Secara teori, program akan dapat memuat semua data pra-inisialisasi dari file yang dapat dieksekusi ke dalam RAM, pada saat yang sama ketika program itu sendiri diunggah ke RAM.

Sejak itu, komputer dan OS telah berevolusi, dan C digunakan di area yang jauh lebih luas dari yang diperkirakan semula. OS PC modern memiliki alamat virtual dll, dan semua sistem tertanam menjalankan kode dari ROM, bukan RAM. Jadi ada banyak situasi di mana RAM tidak dapat diatur "secara otomatis".

Juga, standarnya terlalu abstrak untuk mengetahui apa-apa tentang tumpukan dan memproses memori dll. Hal-hal ini harus dilakukan juga, sebelum program dimulai.

Oleh karena itu, hampir setiap program C / C ++ memiliki beberapa kode init / "copy-down" yang dieksekusi sebelum main dipanggil, agar sesuai dengan aturan inisialisasi standar.

Sebagai contoh, sistem embedded biasanya memiliki opsi yang disebut "startup non-ISO compliant" di mana seluruh fase inisialisasi dilewati karena alasan kinerja, dan kemudian kode sebenarnya dimulai langsung dari utama. Tetapi sistem seperti itu tidak sesuai dengan standar, karena Anda tidak dapat mengandalkan nilai init dari variabel global / statis.

Lundin
sumber
4

"Program" Anda hanya mengembalikan nilai dari variabel global. Yang lainnya adalah kode inisialisasi. Dengan demikian, standar berlaku - Anda hanya memiliki program yang sangat sepele dan inisialisasi yang lebih kompleks.

Zac Howland
sumber
2

Sepertinya semantik bahasa Inggris berdalih. OP merujuk pada blok kode-nya terlebih dahulu sebagai "kode" dan kemudian sebagai "program." Pengguna menulis kode, dan kemudian kompiler menulis program.

dSerk
sumber
1

main dipanggil setelah menginisialisasi semua variabel global.

Yang tidak ditentukan oleh standar adalah urutan inisialisasi semua variabel global semua modul dan pustaka yang terhubung secara statis.

vz0
sumber
0

Ya, utama adalah "titik masuk" dari setiap program C ++, kecuali ekstensi spesifik implementasi. Meski begitu, beberapa hal terjadi sebelum main, terutama inisialisasi global seperti untuk main_ret.

Fred Nurk
sumber