İs 'int main;' geçerli bir C / C ++ programı?


113

Soruyorum çünkü derleyicim öyle düşünüyor gibi görünüyor, ben düşünmüyorum.

echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall

Clang bununla ilgili hiçbir uyarı veya hata vermez ve gcc yalnızca uysal uyarıyı verir: 'main' is usually a function [-Wmain]ancak yalnızca C olarak derlendiğinde a belirtmek -std=önemli görünmüyor.

Aksi takdirde, iyi derler ve bağlanır. Ancak yürütme anında SIGBUS(benim için) ile hemen sona eriyor .

(Mükemmel) cevapları okumak, hangi işareti C ve C ana () dönüş ++? ve dil özellikleri sayesinde hızlı bir grep, kesinlikle olur görünmek ana bana işlevi gereklidir. Ancak gcc'nin lafzı -Wmain('ana' genellikle bir işlevdir) (ve buradaki hataların eksikliği) muhtemelen aksini gösteriyor gibi görünüyor.

Ama neden? Bunun için garip bir durum veya "tarihsel" kullanım var mı? Ne verir bilen var mı?

Demek istediğim, bunun gerçekten barındırılan bir ortamda bir hata olması gerektiğini düşünüyorum , değil mi?


6
gcc -std=c99 -pedantic ...
Gcc'yi

3
@pmg Onun aynı uyarı ile veya olmadan -pedanticya da herhangi -std. Sistemim c99de bunu uyarı veya hata olmadan
Geoff Nixon

3
Ne yazık ki, eğer "yeterince zeki" iseniz, derleyici tarafından kabul edilebilir ancak mantıklı olmayan şeyler yaratabilirsiniz. Bu durumda, C çalışma zamanı kitaplığını main, çalışması olası olmayan adlı bir değişkeni çağırmak için bağlarsınız. Main'i "doğru" değerle başlatırsanız, aslında geri dönebilir ...
Mats Petersson

7
Ve geçerli olsa bile, yapılması korkunç bir şeydir (okunamayan kod). BTW, barındırılan uygulamalarda ve bağımsız uygulamalarda (hakkında bilmeyen main) farklı olabilir
Basile Starynkevitch

1
Daha eğlenceli zamanlar için deneyinmain=195;
imallett

Yanıtlar:


97

Soru C ve C ++ olarak çift etiketlendiğinden, C ++ ve C için mantık farklı olacaktır:

  • C ++, bağlayıcının farklı türlerdeki metinsel olarak özdeş sembolleri ayırt etmesine yardımcı olmak için ad karıştırma kullanır, örneğin bir global değişken xyzve bağımsız bir global işlev xyz(int). Ancak isim mainasla karıştırılmaz.
  • C karıştırmayı kullanmaz, bu nedenle bir programın farklı bir sembol yerine bir tür sembol sağlayarak bağlayıcıyı karıştırması ve programın başarılı bir şekilde bağlanmasını sağlaması mümkündür.

Burada olan şey budur: bağlayıcı sembol bulmayı bekler ve bulur main. Bu sembolü bir işlevmiş gibi "bağlar", çünkü daha iyisini bilmiyor. Çalışma zamanı kitaplığının kontrolü mainbağlayıcıya soran kısmı main, bu nedenle bağlayıcı ona sembol verir mainve bağlantı aşamasının tamamlanmasına izin verir . Elbette bu, mainbir işlev olmadığı için çalışma zamanında başarısız olur .

İşte aynı sorunun başka bir örneği:

dosya xc:

#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
    printf("%p\n", (void*)&foo);
    return 0;
}

dosya yc:

int foo; // <<== external definition supplies a symbol of a wrong kind

derleme:

gcc x.c y.c

Bu derlenir ve muhtemelen çalışır, ancak tanımsız bir davranıştır çünkü derleyiciye vaat edilen sembolün türü, bağlayıcıya sağlanan gerçek sembolden farklıdır.

Uyarı gittiği sürece makul olduğunu düşünüyorum: C mainişlevi olmayan kitaplıklar oluşturmanıza izin verir , böylece bilinmeyen bir nedenle mainbir değişken tanımlamanız gerekirse derleyici adı diğer kullanımlar için serbest bırakır main.


3
Yine de, C ++ derleyicisi ana işlevi farklı şekilde ele alır. Adı "C" haricinde bile karıştırılmaz. Sanırım bunun nedeni, aksi takdirde bağlantı sağlamak için kendi harici "C" main'ini yayması gerekmesidir.
UldisK

@UldisK Evet, bunu ben de fark ettim ve oldukça ilginç buldum. Mantıklı ama bunu hiç düşünmemiştim.
Geoff Nixon

2
Aslında, burada belirtildiği gibi, C ++ ve C için sonuçlar farklı değildir - mainbir işlev olsun ya da olmasın, C ++ 'da adın karıştırılmasına (yani öyle görünüyor) tabi değildir.
Geoff Nixon

4
@nm Soruyu yorumlamanızın çok dar olduğunu düşünüyorum: soruyu gönderinin başlığında sormaya ek olarak OP, programının neden ilk başta derlendiğine dair açıkça bir açıklama istiyor ("derleyicim öyle düşünüyor, yapmasam bile ") yanı sıra mainbir işlevden başka bir şey olarak tanımlamanın neden yararlı olabileceğine dair bir öneri . Cevap, her iki bölüm için de bir açıklama sunar.
dasblinkenlight

1
Ana sembolün isim bozmaya tabi olmaması konu dışıdır. C ++ standardında adın karıştırılmasından bahsedilmez. İsim değiştirme bir uygulama sorunudur.
David Hammen

30

mainBir değil özel amaçlı sözcük bu sadece bir değil , önceden tanımlanmış tanımlayıcı (gibi cin, endl, nposadlı bir değişkeni bildirmek böylece ...), mainonun değerini yazdırmak sonra başlaması ve.

Elbette:

  • bu oldukça hataya açık olduğundan uyarı yararlıdır;
  • main()fonksiyon (kitaplıklar) olmadan bir kaynak dosyanıza sahip olabilirsiniz .

DÜZENLE

Bazı referanslar:

  • main ayrılmış bir kelime değil (C ++ 11):

    Fonksiyon main, bir program içerisinde kullanılmayacaktır. Bağlantısı (3.5) mainuygulama tanımlıdır. Silinmiş veya ana beyan ana tanımlayan bir program olarak inline, staticya da constexprkötü oluşturulur. İsim mainbaşka türlü rezerve edilmemiştir. [Örnek: Üye işlevler, sınıflar ve numaralandırmalar, maindiğer ad alanlarındaki varlıklar gibi çağrılabilir . - son örnek]

    C ++ 11 - [basic.start.main] 3.6.1.3

    [2.11 / 3] [...] bazı tanımlayıcılar C ++ uygulamaları ve standart kitaplıklar (17.6.4.3.2) tarafından kullanılmak üzere ayrılmıştır ve başka türlü kullanılmayacaktır; teşhis gerekmez.

    [17.6.4.3.2 / 1] Belirli adlar ve işlev imzaları her zaman uygulama için ayrılmıştır:

    • Çift alt çizgi __ içeren veya alt çizgiyle başlayan ve ardından büyük harfle (2.12) başlayan her ad, herhangi bir kullanım için uygulamaya ayrılmıştır.
    • Alt çizgiyle başlayan her ad, genel ad alanında bir ad olarak kullanılmak üzere uygulamaya ayrılmıştır.
  • Programlama dillerinde ayrılmış kelimeler .

    Ayrılmış sözcükler programcı tarafından yeniden tanımlanmayabilir, ancak önceden tanımlanmış sözcükler genellikle bazı kapasitelerde geçersiz kılınabilir. Durum şu main: Bu tanımlayıcıyı kullanan bir bildirimin anlamını yeniden tanımladığı kapsamlar vardır.


- Sanırım bu kadar hataya meyilli olduğu için , bunun neden bir uyarı olduğu (hata değil) ve neden sadece C olarak derlendiğinde bir uyarı olduğu gerçeğiyle şaşırdım - Elbette, olmadan derleme yapabilirsiniz bir main()işlev, ancak onu bir program olarak bağlayamazsınız. Burada olan şey, "geçerli" bir programın a main(), sadece a olmadan bağlanmasıdır main.
Geoff Nixon

7
cinve endlvarsayılan ad alanında değildir - ad alanında bulunurlar std. nposüyesidir std::basic_string.
AnotherParker

1
main olan global bir isim olarak saklıdır. Bahsettiğiniz diğer şeylerin hiçbiri mainönceden tanımlanmamıştır.
Potatoswatter

1
Neye mainizin verildiğine ilişkin sınırlamalar için C ++ 14 §3.6.1 ve C11 §5.1.2.2.1'e bakın . C ++, "Bir uygulama, ana işlevi önceden tanımlamaz" der ve C "Uygulama, bu işlev için hiçbir prototip bildirmez" der.
Potatoswatter

@manlio: lütfen neyden alıntı yaptığınızı netleştirin. Düz C'ye gelince, alıntılar yanlıştır. Yani sanırım c ++ standartlarından herhangi biri değil mi?
dhein

19

int main;geçerli bir C / C ++ program?

Bir C / C ++ programının ne olduğu tam olarak belli değil.

int main;geçerli bir C programı?

Evet. Bağımsız bir uygulamanın böyle bir programı kabul etmesine izin verilir. mainbağımsız bir ortamda özel bir anlamı olması gerekmez.

Öyle değil barındırılan bir ortamda geçerlidir.

int main;geçerli bir C ++ program?

Aynen.

Neden çöküyor?

Program mantıklı zorunda değildir senin çevre. Bağımsız bir ortamda programın başlatılması ve sonlandırılması ve bunun anlamı main, uygulama tarafından tanımlanır.

Derleyici beni neden uyarıyor?

Derleyici, uyumlu programları reddetmediği sürece sizi ne isterse konusunda uyarabilir. Öte yandan, uymayan bir programı teşhis etmek için gereken tek şey uyarıdır. Bu çeviri birimi geçerli bir barındırılan programın parçası olamayacağından, bir teşhis mesajı haklı çıkar.

gccBağımsız bir ortam mı yoksa barındırılan bir ortam mı?

Evet.

gcc-ffreestandingderleme bayrağını belgeler . Ekle ve uyarı kaybolur. Örneğin, çekirdek veya aygıt yazılımı oluştururken kullanmak isteyebilirsiniz.

g++böyle bir bayrağı belgelemiyor. Bunu sağlamanın bu program üzerinde bir etkisi yok gibi görünüyor. G ++ tarafından sağlanan ortamın barındırıldığını varsaymak muhtemelen güvenlidir. Bu durumda teşhisin olmaması bir hatadır.


17

Teknik olarak izin verilmediği için bir uyarıdır. Başlangıç ​​kodu "main" sembol konumunu kullanacak ve üç standart argümanla (argc, argv ve envp) ona atlayacaktır. Yapmaz ve bağlantı anında bunun gerçekten bir işlev olup olmadığını veya bu argümanlara sahip olup olmadığını kontrol edemez. Bu aynı zamanda int main (int argc, char ** argv) 'nin çalışmasının nedenidir - derleyici envp argümanını bilmiyor ve sadece kullanılmıyor ve arayan temizleme işlemidir.

Şaka olarak, şöyle bir şey yapabilirsin

int main = 0xCBCBCBCB;

bir x86 makinesinde ve uyarıları ve benzeri şeyleri göz ardı ederek, sadece derlemekle kalmayacak, aynı zamanda gerçekten de çalışacaktır.

Biri buna benzer bir teknik kullanarak doğrudan birden fazla mimaride çalışan bir çalıştırılabilir (tür) yazdı - http://phrack.org/issues/57/17.html#article . Ayrıca IOCCC'yi kazanmak için kullanıldı - http://www.ioccc.org/1984/mullender/mullender.c .


1
"Teknik olarak izin verilmediği için bir uyarıdır" - C ++ 'da geçersizdir.
Şerefe ve hth. - Alf

3
"üç standart argüman (argc, argv ve envp)" - burada muhtemelen Posix standardından bahsediyorsunuz.
Şerefe ve hth. - Alf

Sistemimde (Ubuntu 14 / x64), aşağıdaki satır gcc ile çalışıyor:int main __attribute__ ((section (".text")))= 0xC3C3C3C3;
csharpfolk

@ Cheersandhth.-Alf İlk ikisi standart, üçüncüsü POSIX.
dascandy

9

Geçerli bir program mı?

Hayır.

Yürütülebilir parçası olmadığı için bir program değildir.

Derlemek geçerli mi?

Evet.

Geçerli bir programla kullanılabilir mi?

Evet.

Derlenen tüm kodların geçerli olması için çalıştırılabilir olması gerekmez. Örnekler statik ve dinamik kitaplıklardır.

Etkili bir nesne dosyası oluşturdunuz. Geçerli bir yürütülebilir dosya değildir, ancak başka bir program mainsonuçtaki dosyadaki nesneyi çalışma zamanında yükleyerek ona bağlanabilir .

Bu bir hata mı olmalı?

Geleneksel olarak C ++, kullanıcının geçerli bir kullanımı yokmuş gibi görünen ancak dilin sözdizimine uyan şeyler yapmasına izin verir.

Demek istediğim elbette, bu bir hata olarak yeniden sınıflandırılabilir, ama neden? Bu, uyarının olmamasına hangi amaca hizmet eder?

Bu işlevselliğin gerçek kodda kullanılmasının teorik bir olasılığı olduğu sürece, çağrılan işlevsiz bir nesneye sahip olmanın maindile göre bir hatayla sonuçlanması pek olası değildir .


Adlı harici olarak görünür bir sembol oluşturur main. Adlandırılmış harici olarak görünür bir işlevi olması gereken geçerli bir program ona nasıl bağlanabilir main?
Keith Thompson

@KeithThompson Çalışma zamanında yük. Açıklığa kavuşturacak.
Michael Gazonda

Sembol türleri arasındaki farkı söyleyemediği için olabilir. Bağlama işleri gayet iyi - yürütme (dikkatlice hazırlanmış durum dışında) sağlamaz.
Chris Stratton

1
@ChrisStratton: Bence Keith'in argümanı, sembol çarpılarak tanımlandığı için bağlanmanın başarısız olduğudur ... çünkü "geçerli program" bir mainişlev tanımlamadıkça geçerli bir program olmayacaktır .
Ben Voigt

@BenVoigt Ancak bir kitaplıkta görünürse, bağlantı başarısız olmaz (ve muhtemelen olamaz), çünkü program bağlantı zamanında int main;tanım görünmez.

6

Mevcut dil standartlarına atıfta bulunarak zaten verilmiş olan cevaplara eklemek istiyorum.

İs 'int main;' geçerli bir C programı?

Kısa cevap (bence): Yalnızca uygulamanız "bağımsız yürütme ortamı" kullanıyorsa.

C11'den aşağıdaki alıntılar

5. Çevre

Bir uygulama, C kaynak dosyalarını çevirir ve C programlarını , çeviri ortamı ve yürütme ortamı olarak adlandırılacak olan iki veri işleme sistemi ortamında yürütür [...]

5.1.2 Yürütme ortamları

İki yürütme ortamı tanımlanmıştır: bağımsız ve barındırılan. Her iki durumda da, çalıştırma ortamı tarafından belirlenmiş bir C işlevi çağrıldığında program başlangıcı gerçekleşir.

5.1.2.1 Bağımsız ortam

Bağımsız bir ortamda (C programının yürütülmesinin bir işletim sisteminin herhangi bir yararı olmadan gerçekleşebileceği), program başlangıcında çağrılan işlevin adı ve türü uygulama tanımlıdır.

5.1.2.2 Barındırılan ortam

Barındırılan bir ortamın sağlanması gerekmez, ancak varsa aşağıdaki özelliklere uyacaktır.

5.1.2.2.1 Program başlatma

Program başlangıcında çağrılan işlev main olarak adlandırılır . [...] Dönüş türü bir int ile ve parametresiz [...] veya iki parametre [...] veya eşdeğeri veya başka bir uygulama tanımlı şekilde tanımlanmalıdır.

Bunlardan aşağıdakiler gözlemlenir:

  • Bir C11 programı, bağımsız veya barındırılan bir yürütme ortamına sahip olabilir ve geçerli olabilir.
  • Bağımsız bir tane varsa, bir ana işlevi olması gerekmez.
  • Aksi takdirde, int türünde bir dönüş değeri olan bir tane olmalıdır .

Bağımsız bir yürütme ortamında, bunun başlatmanın gerçekleşmesine izin vermeyen geçerli bir program olduğunu iddia ediyorum, çünkü 5.1.2'de gerektiği gibi bunun için mevcut bir işlev yok. Barındırılan bir yürütme ortamında, kodunuz main adlı bir nesneyi tanıtsa da , bir dönüş değeri sağlayamaz, bu nedenle bunun bu anlamda geçerli bir program olmadığını savunabilirim, ancak daha önce olduğu gibi, program değilse çalıştırılması amaçlanmıştır (örneğin, yalnızca veri sağlamak isteyebilir), o zaman sadece bunu yapmaya izin vermez.

İs 'int main;' geçerli bir C ++ programı?

Kısa cevap (bence): Yalnızca uygulamanız "bağımsız yürütme ortamı" kullanıyorsa.

C ++ 14'ten alıntı

3.6.1 Ana işlev

Bir program, programın belirlenmiş başlangıcı olan main adlı global bir işlevi içerecektir. Bağımsız bir ortamda bir programın bir ana işlevi tanımlamak için gerekli olup olmadığı, uygulama tarafından tanımlanır. [...] dönüş türü int türünde olacaktır, ancak aksi takdirde türü uygulama tanımlıdır. [...] main adı başka türlü saklı değildir.

Burada, C11 standardının aksine, bağımsız yürütme ortamına daha az kısıtlama uygulanır, çünkü hiçbir başlangıç ​​işlevi belirtilmezken, barındırılan bir yürütme ortamı için durum C11 ile hemen hemen aynıdır.

Yine, barındırılan durum için kodunuzun geçerli bir C ++ 14 programı olmadığını iddia ediyorum, ancak bunun bağımsız durum için olduğundan eminim.

Cevabım sadece uygulama ortamını dikkate aldığından, çeviri ortamında meydana gelen isim kargaşası önceden gerçekleştiği için dasblinkenlicht'in cevabının devreye girdiğini düşünüyorum . Burada, yukarıdaki alıntılara bu kadar katı bir şekilde uyulduğundan pek emin değilim.


4

Demek istediğim, bunun gerçekten barındırılan bir ortamda bir hata olması gerektiğini düşünüyorum, değil mi?

Hata sizindir. Sen adında bir işlev belirtmedi maindöner bir o intve denenmiş barındırılan bir ortamda sizin programı kullanmak için.

Adlandırılmış global bir değişkeni tanımlayan bir derleme biriminiz olduğunu varsayalım main. Bu, bağımsız bir ortamda yasal olabilir, çünkü bir programı oluşturan şey, bağımsız ortamlarda uygulamaya bırakılmıştır.

Diyelim mainki bir döndüren intve argüman almayan bir global işlevi tanımlayan başka bir derleme biriminiz var . Bu, barındırılan bir ortamdaki bir programın tam olarak ihtiyaç duyduğu şeydir.

İlk derleme birimini yalnızca bağımsız bir ortamda kullanırsanız ve yalnızca ikincisini barındırılan bir ortamda kullanırsanız her şey yolunda. Ya ikisini de tek bir programda kullanırsanız? C ++ 'da, tek tanım kuralını ihlal ettiniz. Bu tanımlanmamış bir davranıştır. C'de, tek bir sembole yapılan tüm referansların tutarlı olması gerektiğini belirten kuralı ihlal ettiniz; eğer değilse, bu tanımlanmamış bir davranış. Tanımlanmamış davranış, "hapisten bedava çık!" bir uygulamanın geliştiricilerine kart. Tanımlanmamış davranışa yanıt olarak bir uygulamanın yaptığı her şey standartla uyumludur. Uygulama, tanımlanmamış davranışları saptamak şöyle dursun, uyarmak zorunda değildir.

Ya bu derleme birimlerinden yalnızca birini kullanırsanız, ancak yanlış olanı kullanırsanız (yaptığınız şey buydu)? C'de durum nettir. mainBarındırılan bir ortamda işlevin iki standart formdan birinde tanımlanamaması, tanımlanmamış bir davranıştır. Hiç tanımlamadığınızı varsayalım main. Derleyici / bağlayıcı, bu hata hakkında bir şey söylemek zorunda değildir. Şikayet etmeleri kendi adına bir nezakettir. C programının hatasız derlenmiş ve bağlanmış olması derleyicinin değil, sizin hatanızdır.

Bu, C ++ 'da biraz daha az açıktır çünkü mainbarındırılan bir ortamda işlevin tanımlanamaması, tanımsız davranıştan ziyade bir hatadır (başka bir deyişle, teşhis edilmesi gerekir). Bununla birlikte, C ++ 'daki tek tanım kuralı, bağlayıcıların oldukça aptal olabileceği anlamına gelir. Bağlayıcının görevi dış referansları çözmektir ve tek tanım kuralı sayesinde, bağlayıcının bu sembollerin ne anlama geldiğini bilmek zorunda kalmaz. Adlı bir sembol sağladınız main, bağlayıcı, adlandırılmış bir sembolü görmeyi bekliyor main, böylece bağlayıcı söz konusu olduğunda her şey yolunda.


4

Şimdiye kadar C için uygulama tanımlı davranıştır.

ISO / IEC9899'un dediği gibi:

5.1.2.2.1 Program başlatma

1 Program başlangıcında çağrılan işlev main olarak adlandırılır. Uygulama, bu işlev için hiçbir prototip bildirmez. Dönüş türü bir int ile ve parametresiz olarak tanımlanmalıdır:

int main(void) { /* ... */ }

veya iki parametreli (burada argc ve argv olarak anılır, ancak bildirildikleri işlev için yerel oldukları için herhangi bir ad kullanılabilir):

int main(int argc, char *argv[]) { /* ... */ }

veya eşdeğer; veya başka bir uygulama tanımlı şekilde.


3

Hayır, bu geçerli bir program değil.

C ++ için bu, yakın zamanda kusur raporu 1886 tarafından açıkça biçimsiz hale getirildi : main () için dil bağlantısı, diyor ki:

Main () 'e açık bir dil bağlantısı vermede herhangi bir kısıtlama yok gibi görünmektedir, ancak muhtemelen ya biçimsiz ya da koşullu olarak desteklenmelidir.

ve kararın bir kısmı aşağıdaki değişikliği içeriyordu:

Global kapsamda main değişkenini bildiren veya main adını C dili bağlantısıyla (herhangi bir isim alanında) bildiren bir program bozuk biçimlidir.

Bu ifadeyi , C ++ 1z taslağı olan en son C ++ taslak standardı N4527'de bulabiliriz.

Hem clang hem de gcc'nin en son sürümleri artık bunu bir hata yapıyor ( canlı yayına bakın ):

error: main cannot be declared as global variable
int main;
^

Bu hata raporundan önce, teşhis gerektirmeyen tanımsız bir davranıştır. Öte yandan, kötü biçimlendirilmiş kod bir teşhis gerektirir, derleyici bunu bir uyarı veya bir hata yapabilir.


Güncelleme için teşekkürler! Bunun artık derleyici tanılamasıyla anlaşıldığını görmek harika. Ancak, C ++ standardındaki değişiklikleri şaşırtıcı bulduğumu söylemeliyim. (Arka plan için adı bozma ilgili yukarıdaki yorumları görmek main()Ben izin vermeme gerekçesini anlıyoruz.) main()Açık bir bağlantı-şartname sahip olmaktan, ama yok mandating bunu anlamak main()zorunda C ++ bağlantı . Tabii ki standart (Itanium ABI'lı diyelim ki) doğrudan ABI bağlantı / ad bozma nasıl işleneceğini adresi, ama pratikte bu bozmak olurdu değil main()hiç _Z4mainv. Neyi kaçırıyorum?
Geoff Nixon

Sanırım Supercat'ın yorumu bunu kapsıyor. Uygulama, kullanıcı tanımlı main'i çağırmadan önce kendi işini yapıyorsa, bunun yerine kolayca karıştırılmış bir ad çağırmayı seçebilir.
Shafik Yaghmour
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.