Main () gerçekten bir C ++ programının başlangıcı mı?


131

C ++ Standardının 3.6.1 / 1 dolarlık bölümünde,

Bir program, programın belirlenmiş başlangıcı olan main adlı genel bir işlevi içerecektir .

Şimdi bu kodu düşünün,

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;
}

Bu örnek kod , programın "başlangıcı" olması gereken işleve girmeden önce , 0'dan 9'a kadar olan tam sayıların karesini yazdırmak gibi, yapmayı planladığım şeyi yapar main().

Bunu ayrıca -pedanticGCC 4.5.0 seçeneği ile derledim . Hata yapmaz, uyarı bile vermez!

Yani sorum şu:

Bu kod gerçekten Standart uyumlu mu?

Standart uyumluysa, Standardın söylediği şeyi geçersiz kılmaz mı? main()bu programın başlangıcı değil! user_main()önce idam edildi main().

Küresel değişkeni başlatmak için main_retönce use_main()çalıştıranın ancak bunun tamamen farklı bir şey olduğunu anlıyorum ; nokta, yani yok gibi $ 3.6.1 / 1 Standard alıntı deyimi geçersiz main()DEĞİLDİR başlangıç programının; o aslında arasında bu programda!


DÜZENLE:

'Başlangıç' kelimesini nasıl tanımlarsınız?

Bu, "programın başlangıcı" ifadesinin tanımına indirgenir . Peki bunu tam olarak nasıl tanımlıyorsunuz?

Yanıtlar:


85

Hayır, C ++ main çağrısından önce "ortamı ayarlamak" için pek çok şey yapar; ancak, main, C ++ programının "kullanıcı tarafından belirtilen" kısmının resmi başlangıcıdır.

Ortam kurulumunun bir kısmı kontrol edilemez (std :: cout'u kurmak için ilk kod gibi; bununla birlikte, ortamın bir kısmı statik global bloklar gibi kontrol edilebilir (statik global değişkenleri başlatmak için). ana kontrol öncesi kontrol, statik blokların başlatılma sırası üzerinde tam kontrole sahip değilsiniz.

Temelden sonra, kodunuz kavramsal olarak programın "tamamen kontrolündedir", yani hem gerçekleştirilecek komutları hem de bunları gerçekleştirme sırasını belirtebilirsiniz. Çoklu iş parçacığı, kod yürütme sırasını yeniden düzenleyebilir; ancak yine de C ++ ile kontrolün altındasınız çünkü kod bölümlerinin sırasız (muhtemelen) çalıştırılacağını belirttiniz.


9
Bunun için +1 "Main'den önce tam kontrole sahip olmadığınız için, statik blokların başlatılma sırası üzerinde tam kontrole sahip olmadığınızı unutmayın. Main'den sonra, kodunuz kavramsal olarak" tamamen kontrol altında " program, hem gerçekleştirilecek talimatları hem de bunları gerçekleştirme sırasını belirleyebilmeniz açısından " . Bu aynı zamanda bu cevabı kabul edilmiş cevap olarak işaretlememe neden oluyor ... Bunların çok önemli noktalar olduğunu düşünüyorum, bu main()da "programın başlangıcı"
Nawaz

13
@Nawaz: Başlatma sırası üzerinde tam bir kontrolün olmamasına ek olarak, başlatma hataları üzerinde hiçbir kontrolünüzün olmadığına dikkat edin: genel kapsamda istisnaları yakalayamazsınız.
André Caron

@Nawaz: Statik küresel bloklar nedir? basit bir örnekle açıklar mısınız lütfen? Teşekkürler
Destructor

@meet: İsim alanı düzeyinde bildirilen nesnelerin staticdepolama süresi vardır ve bu nedenle, farklı çeviri birimlerine ait olan bu nesneler herhangi bir sırayla başlatılabilir (çünkü sıra standart tarafından belirtilmemiştir ). Sorunuzun cevabının bu olduğundan emin değilim, ancak bu konu bağlamında söyleyebileceğim şey bu.
Nawaz

88

Cümleyi yanlış okuyorsunuz.

Bir program, programın belirlenmiş başlangıcı olan main adlı genel bir işlevi içerecektir .

Standart, standardın geri kalanının amaçları için "başlangıç" kelimesinin TANIMLANMASIdır. Daha önce hiçbir kodun çalıştırılmadığını söylemez main. Programın başlangıcının işlevde olduğu kabul edildiğini söylüyor main.

Programınız uyumludur. Programınız main başlatılana kadar "başlamadı". Kurucu, standarttaki "start" tanımına göre programınız "başlamadan" önce çağrılır, ancak bu pek önemli değildir. Bir kod LOT önce yürütülür mainedilir zamankinden sadece bu örnek, her programda denir.

Tartışma amacıyla, kurucu kodunuz programın 'başlangıcından' önce yürütülür ve bu, standartla tamamen uyumludur.


3
Üzgünüm ama bu maddeyi yorumlamanıza katılmıyorum.
Orbit'te Hafiflik Yarışları

Bence Adam Davis haklı, "ana" daha çok bir çeşit kodlama kısıtlaması gibi.
laike9m

@LightnessRacesinOrbit Hiçbir zaman takip etmedim, ancak bana göre bu cümle mantıksal olarak "main adlı global bir işlev programın belirlenmiş başlangıcıdır " (vurgu eklenmiştir) şeklinde özetlenebilir. Bu cümle hakkındaki yorumunuz nedir?
Adam Davis

1
@AdamDavis: Endişemin ne olduğunu hatırlamıyorum. Şimdi düşünemiyorum.
Orbit'te Hafiflik Yarışları

23

Programınız bağlantı kurmayacak ve bu nedenle bir ana yoksa çalışmayacaktır. Bununla birlikte main (), programın yürütülmesinin başlamasına neden olmaz çünkü dosya seviyesindeki nesneler önceden çalışan kuruculara sahiptir ve main () 'e ulaşılmadan önce ömrünü çalıştıran bir programın tamamını yazmak ve main'in boş bir vücut.

Gerçekte bunu uygulamak için main'den önce oluşturulmuş bir nesneye ve programın tüm akışını çağırmak için kurucusuna sahip olmanız gerekir.

Şuna bak:

class Foo
{
public:
   Foo();

 // other stuff
};

Foo foo;

int main()
{
}

Programınızın akışı etkili bir şekilde aşağıdakilerden kaynaklanacaktır: Foo::Foo()


13
+1. Ancak, farklı çeviri birimlerinde birden fazla global nesneniz varsa, kurucuların çağrılma sırası tanımsız olduğundan, bu sizi kısa sürede başınızı belaya sokar. Tekil ve tembel başlatma ile kurtulabilirsiniz, ancak çok iş parçacıklı bir ortamda işler çok çabuk çirkinleşir. Tek kelimeyle, bunu gerçek kodla yapmayın.
Alexandre C.

3
Muhtemelen kodunuzda main () 'e uygun bir gövde vermeniz ve yürütmeyi çalıştırmasına izin vermeniz gerekirken, bu başlangıç ​​dışındaki nesneler kavramı birçok LD_PRELOAD kitaplığının dayandığı şeydir.
CashCow

2
@Alex: Standart tanımsız diyor, ancak pratik bir konu bağlantı sırası (genellikle derleyiciye bağlı olarak) kontrol başlatma sırasını belirler.
ThomasMcLeod

1
@Thomas: Kesinlikle buna uzaktan bile güvenmeyi denemem. Ayrıca, derleme sistemini manuel olarak kontrol etmeye çalışmazdım.
Alexandre C.

1
@Alex: artık çok önemli değil, ancak gün içinde, fiziksel bellek sayfalandırmasını azaltmak için derleme görüntüsünü kontrol etmek için bağlantı sırasını kullanırdık. Başlatma performans karşılaştırma testi gibi program anlamını etkilemese bile başlatma sırasını kontrol etmek isteyebileceğiniz başka yan nedenler de vardır.
ThomasMcLeod

15

Soruyu da "C" olarak etiketlediniz, sonra, kesinlikle C hakkında konuşursak, ISO C99 standardının 6.7.8 "Başlatma" bölümüne göre başlatmanız başarısız olacaktır.

Bu durumda en alakalı olanı, şunu söyleyen 4 numaralı kısıt gibi görünüyor:

Statik depolama süresi olan bir nesne için bir başlatıcıdaki tüm ifadeler, sabit ifadeler veya dize değişmezleri olacaktır.

Yani sorunuzun cevabı, kodun C standardına uygun olmamasıdır.

Yalnızca C ++ standardıyla ilgileniyorsanız, muhtemelen "C" etiketini kaldırmak istersiniz.


4
@ Remo.D bize bu bölümde ne olduğunu söyleyebilir misiniz? Hepimizin C standardı yok :).
UmmaGumma

2
Çok seçici olduğunuz için: Ne yazık ki ANSI C, 1989'dan beri kullanılmıyor. ISO C90 veya C99, alıntı yapılacak ilgili standartlardır.
Lundin

@Lundin: Hiç kimse yeterince seçici değil :) ISO C99 okuyordum ama C90 için de geçerli olduğuna eminim.
Remo.D

@Bir atış. Haklısın, burada en alakalı olduğunu düşündüğüm cümleyi ekledim.
Remo.D

3
@Remo: Geçerli olmadığı bilgisini sağlamak için +1 C; bunu bilmiyordum. İnsanların bazen planla, bazen de şans eseri nasıl öğrendiğini görün!
Nawaz

10

Bölüm 3.6, bir bütün olarak, etkileşim mainve dinamik ilklendirmeler hakkında çok nettir . "Programın belirlenmiş başlangıcı" başka hiçbir yerde kullanılmaz ve yalnızca genel amacını açıklar main(). Bu tek bir cümleyi Standarttaki daha detaylı ve açık gerekliliklerle çelişen normatif bir şekilde yorumlamanın hiçbir anlamı yoktur.


9

Derleyicinin standart uyumlu olması için genellikle main () 'den önce kod eklemesi gerekir . Çünkü standart, globallerin / statiklerin başlatılmasının program çalıştırılmadan önce yapılması gerektiğini belirtir . Ve belirtildiği gibi, aynı şey dosya kapsamına yerleştirilen nesnelerin yapıcıları (küreseller) için de geçerlidir.

Böylece orijinal soru olan bir C programında yine program başlamadan önce yapılacak globaller / statik başlatma olurdu, çünkü sıra C'ye alakalı.

Standartlar, bu değişkenlerin "sihir" yoluyla başlatıldığını varsayar, çünkü program başlatmadan önce nasıl ayarlanmaları gerektiğini söylemezler . Sanırım bunu bir programlama dili standardının kapsamı dışında bir şey olarak gördüler.

Düzenleme: Örneğin bkz. ISO 9899: 1999 5.1.2:

Statik depolama süresi olan tüm nesneler, program başlatılmadan önce başlatılmalıdır (başlangıç ​​değerlerine ayarlanmalıdır). Bu tür bir başlatmanın tarzı ve zamanlaması başka türlü belirtilmemiştir.

Bu "sihrin" nasıl yapılacağının ardındaki teori, C'nin, RAM tabanlı bilgisayarlarda yalnızca UNIX OS için kullanılması amaçlanan bir programlama dili olduğu zamana kadar uzanıyor. Teorik olarak, program önceden başlatılmış tüm verileri çalıştırılabilir dosyadan RAM'e, programın kendisi RAM'e yüklenirken aynı anda yükleyebilecektir.

O zamandan beri bilgisayarlar ve işletim sistemi gelişti ve C başlangıçta tahmin edilenden çok daha geniş bir alanda kullanıldı. Modern bir PC işletim sisteminin sanal adresleri vb. Vardır ve tüm gömülü sistemler kodu RAM'den değil ROM'dan yürütür. Dolayısıyla, RAM'in "otomatik olarak" ayarlanamadığı birçok durum vardır.

Ayrıca, standart yığınlar ve işlem belleği vb. Hakkında herhangi bir şey bilemeyecek kadar soyuttur. Bunlar da program başlamadan önce yapılmalıdır.

Bu nedenle, hemen hemen her C / C ++ programı, standartların başlatma kurallarına uymak için main çağrılmadan önce çalıştırılan bazı init / "copy-down" kodlarına sahiptir.

Örnek olarak, gömülü sistemler tipik olarak "ISO uyumlu olmayan başlatma" olarak adlandırılan bir seçeneğe sahiptir; burada tüm başlatma aşaması performans nedenleriyle atlanır ve ardından kod aslında doğrudan ana bilgisayardan başlar. Ancak bu tür sistemler, global / statik değişkenlerin init değerlerine güvenemeyeceğiniz için standartlara uymaz.


4

"Programınız" basitçe genel bir değişkenden bir değer döndürür. Diğer her şey başlatma kodudur. Bu nedenle, standart geçerlidir - sadece çok önemsiz bir programınız ve daha karmaşık bir başlatmanız var.



2

İngilizce semantik kelime oyunu gibi görünüyor. OP, kendi kod bloğuna önce "kod" ve daha sonra "program" olarak atıfta bulunur. Kullanıcı kodu yazar ve ardından derleyici programı yazar.


1

main, tüm global değişkenleri başlattıktan sonra çağrılır.

Standardın belirtmediği şey, tüm modüllerin ve statik olarak bağlantılı kitaplıkların tüm global değişkenlerinin başlatılma sırasıdır.


0

Evet, uygulamaya özgü uzantılar dışında, ana her C ++ programının "giriş noktası" dır. Öyle bile olsa, main_ret için olduğu gibi, ana, özellikle küresel ilklendirmeden önce bazı şeyler olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.