# Ständge C++ Fehlerausgabe bei int main()



## bratak1991 (17. März 2010)

*Ständge C++ Fehlerausgabe bei int main()*

Hallo liebe Programmierer

ich habe ein Problem bei meiner C++ Übung.

Ich sitze gerade an Eclipse (ich arbeite in einer von meinem Professor komplett eingerichteten Virtual Machine), und wollte ein kleines Programm zur Dreiecksberechnung schreiben. Nun kommt aber bei jedem Versuch ein und die Selbe Fehlermeldung:


```
Undefined Reference to main
```

Meine Arbeit sieht bisher wie folgt aus:


```
/*
 * dreieck.C
 *
 *  Created on: 17.03.2010
 *      Author: imbi
 */

#include <iostream>
using namespace std;

//Deklaration
double grundseite;
double hoehe;
double flaeche;

int main() {
    //Eingabe
    cout << "Dreicksfläche berechnen" << endl;
    cout << "Grundseite=" << grundseite << flush;
    cin >> grundseite;
    cout << "Hoehe=" << hoehe << flush;
    cin >> hoehe;
    //Verarbeitung
    flaeche=grundseite*hoehe*0.5;
    //Ausgabe
    cout << "Dreiecksflaeche=" << flaeche << endl;

    return 0;

}
```

Meine Frage: was mache ich falsch? es kann doch nur irgendetwas ganz simples sein, oder?
Die Includes sind ja schon alle da.

Für schnelle Hilfe wäre ich dankbar!


----------



## bingo88 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

versuch mal bitte

```
int main(int argc, char *argv[])
```
int main(void) ist nämlich eigentlich nicht zulässig!


----------



## Bauer87 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Zumindest der gcc kompiliert deinen Code ohne Murren. Allerdings hast du drei keine Probleme, wenn man ganz pingelig ist:


Offenbar nennst du deine Datei mit c++-Code dreieck.c. Das heißt, der Compiler könnte es je nach Aufruf als c-Code interpretieren. Das klappt natürlich nicht.
Es gibt kein Newline am Ende. (Das letzte Zeichen soll nach Standard immer ein Zeilenumbruch sein, C++-Quellcode endet immer mit einer leeren Zeile.)
main nimmt normalerweise noch Argumente entgegen. Je nach Compiler kann es auch da zu Problemen kommen. Korrekt lautet es: 
	
	



```
int main(int argc, char** argv)
```

Mit welchem Befehl rufst du denn den Compiler auf?


----------



## bingo88 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Ohne Newline am Ende gibt's bei Eclipse echt Probleme (die Produkte von Onkel Microsoft sehen über solche "Lappalien" hinweg - kosten ja auch genug Kohle ).
Irgendwie halten sich int main(void) bzw. void main(void) und die diversen Abwandlungen hartnäckig...


----------



## Bauer87 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

void main() wird von gcc aber auch nicht mehr übersetzt, es muss schon ein int (*)() sein. Über Verletzungen des Standard hinweg zu sehen, halte ich aber für eher schlecht. somit gewöhnt man sich machen an und der Code lässt sich am Ende womöglich nur noch mit einem bestimmten Compiler übersetzen. (Das ist dann vor allem blöd, wenn der Intel-Compiler wegfällt. Der optimiert echt gut, wenn man x86-Maschinen nutzt.)


----------



## bratak1991 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

funzt leider alles noch nicht.


----------



## Bauer87 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Häng mal die originale Datei an.


----------



## bingo88 (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*



Bauer87 schrieb:


> void main() wird von gcc aber auch nicht mehr übersetzt, es muss schon ein int (*)() sein. Über Verletzungen des Standard hinweg zu sehen, halte ich aber für eher schlecht. somit gewöhnt man sich machen an und der Code lässt sich am Ende womöglich nur noch mit einem bestimmten Compiler übersetzen. (Das ist dann vor allem blöd, wenn der Intel-Compiler wegfällt. Der optimiert echt gut, wenn man x86-Maschinen nutzt.)


Das wollte ich ja auch garnicht unterstützen! Ich bin eher einer der ersten, die sich über sowas aufregen 
Über den intel-Compiler kann ich wenig sagen, da es den für Win nicht umsonst gibt und ich auch ne AMD Plattform besitze und da war was mit CPUID-abhängigen "Optimierungen"  Bis jetzt konnte ich die noch selnbst über Instrincts bzw. direkt Assembler einbinden. Dauert zwar länger aber ich weiß was ich tue (und es läuft auf meinem Athlon)


----------



## Puepue (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Visual Studio C++ Express Edition sagt: 0 Fehler, 0 Warnungen, 0 Meldungen


----------



## Puepue (17. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*



> Irgendwie halten sich int main(void) bzw. void main(void) und die diversen Abwandlungen hartnäckig...




Bin zwar noch nicht soooo lange dabei aber uns wurde das so beigebracht o0
(funktioniert mit unseren Programmen ja auch ^^)


----------



## bingo88 (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Och nööö...
mich wundert halt, warum das überhaupt gemacht wird. Das Weglassen der Parameter kann ich ja noch iwo nachvollziehen (wenn man sie halt nicht braucht). Auch sind die Auswirkungen da recht harmlos bzw. nicht vorhanden.
Ich schweife mal "kurz" was ab in die wunderbare Theorie 

Es gibt da so ne nette Geschichte namens Aufrufkonventionen. Wie man aus dem Namen deuten könnte, handelt es sich dabei um die Art, wie Parameter an eine Funktion übergeben werden. Ich werde mich im folgenden mal auf C beschränken, in C++ läuft's aber ähnlich.

Nehmen wir die Funktion

```
void foo(int bar)
```
Der Compiler übersetzt das in Assembler so (Standard C Calling Convention):

```
_foo:
    push ebp         ; Basepointer sichern
    mov ebp, esp   ; Stackpointer in das EBP-Register kopieren

    ; bla bla bla hier der Funktionscode

    pop ebp          ; Basepointer wiederherstellen
    ret                 ; Funktion verlassen (Rücksprung zum Aufrufer)
```
Unsere nette Variable bar vom Typ int wäre im asm-Programm nun an der Adresse ebp + 8 zu erreichen (32 Bit System daher Adresse (int) 4 Byte lang). Woher kommt's?

Argumente für Funktionen werden bei der C Calling Convention von rechts nach links auf dem Stack abgelegt (ESP = Stackpointer; der zählt runter!). Wenn foo jetzt die Argumente bar1 und bar2 hätte, dann würden die Parameter also in der Reihenfolge bar2, bar1 auf dem Stack landen. Das erste Funktionsargument wird also zuletzt auf den Stack gepusht und hätte die Adresse ESP + 4. Darum funktioniert zum Beispiel die variable Argumentenliste bei printf, so nebenbei bemerkt 

Aber warum ist die Adresse jetzt ESP + 4, wenn doch eigentlich ESP die aktuelle Stackposition beschreibt? Es wird noch implizit ein weiterer Parameter auf dem Stack abgelegt: Die Rücksprungadresse! Das ist die Adresse, die der Aufrufer der Funktion mitteilt, welche mit ret dann am Ende angesprungen wird. Das ist übrigens auch ne nette Stelle zum reinhacken mittels Bufferoverflows, würde jetzt aber viel zu weit führen! Bei Interesse könnte ich dazu aber auch mal was erzählen 

Dieses Konstrukt mit "push ebp" und "mov ebp, esp" dient nur dazu, einfacher lokal auf dem Stack angelegte Variablen anzusprechen (grob gesagt!). Durch das "push ebp" wird ESP wieder um 4 Bytes kleiner, daher ist das neue Layout:

```
ESP = Adresse vom "alten" EBP
ESP + 4 = Rücksprungadresse
ESP + 8 = Adresse erstes Argument
ESP + 12 = Adresse zweites Argument
ESP + 16 = Adresse drittes Argument
...
```
Der Teil, über den ich bis jetzt gesprochen habe, ist nicht so wichtig. Wenn man nämlich die Parameter weglässt (bei der Funktionsdefinition/deklaration), dann gibt es den ganzen Kram einfach nicht. Aber ich brauche das als Vorspiel für das eigentliche Problem: void main(...)!

Nach Definition wird von der main-Funktion ein Wert (ein int) als Rückgabewert erwartet - und zwar von einer "höhere Instanz", auf die du als Entwickler keinen Einfluss hast. Wenn du jetzt nen void machst, sieht der Code im Prinzip wie oben aus, also ab dem Abschnitt "pop ebp" und "ret". Dummerweise sieht die C Calling Convention aber vor, das Rückgabewerte im Register EAX stehen. Bei der normalen main würde das dann so aussehen:

```
; wunderschöner asm code...
     
     mov eax, 0    ; das ist dein "return 0;"
     pop ebp
     ret
```
Wenn du das jetzt weglässt, hast du in EAX irgendeinen Müll stehen und deine "höhere Instanz" bekommt diesen Müll zu schlucken! Klingt schon problematisch, kann es auch sein! Das kann zu einfachen Programmfehlern bis hin zu Abstürzen führen.

Ich behaupte jetzt mal, dass das hier schon mehr ist, als man als Entwickler eigentlich wissen muss (außer man arbeitet mit Assembler ). Also wenn du jetzt nur Bahnhof verstanden hast, ist das nicht so tragisch 

Nun hängt das auch vom verwendeten Compiler/Linker ab, ob void main(...) fehlerfrei kompiliert. Diesen Kram da oben kann man nämlich prüfen - muss man aber nicht


----------



## DarkMo (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

ie frage is doch ganz simpel: brauch man nen rückgabewert? wenn ich sinnlosest return 0; schreiben muss, nur um die blöde konvention zu erfüllen, ohne das das ganze im programm irgendeinen tieferen oder nur gröberen sinn hätte - wozu? wenn ich ein kleines proggi schreibe von dem ich weis, das wird nie so verwendet, dass da eine höhere instanz nen rückgabewert brauch - wieso sollte ich den anfügen? is doch totaler käse. ich bau in ner setmethode ja auch keinen return wert sein - falls mal irgendeine andre methode oder funktion oder so da nen wert wöllt 

wenn ich das alles selber schreibe und ich hier nie mit irgendwas andrem kommuniziere, isses schlicht und ergreifend überflüssig. frag mich also ernsthaft, wieso man künstlich son return gefimmel einbauen muss. da steht dann halt im assemblercode im return wert irgendwelcher müll (die adresse vom ersten bildschnipsel des neusten natur und heimatkunde filmchens? ^^) - aber wen scherts denn? wird doch eh nie weitergegeben ^^


----------



## bingo88 (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Es wird den Leuten aber so beigebracht und meiner Erfahrung nach merken die Leute sich das dann auch! Das stellt ein Problem dar. Normalerweise regelt der Compiler ja über Header-Dateien etc. was man wie aufzurufen hat, aber die Main hat halt keinen Header, sondern es ist eine Definition - es wird einfach verlangt, dass die einen Wert zurück liefert. Hier im Forum streiten sich Leute über Intel vs. AMD bzw. AMD vs. Nvidia aber bei so ner Sache heißt es nur is ja nich so schlimm  Ich verstehe das nicht, die eine Zeile mehr kann doch keine Faulheit sein...

Problematisch wird's dann, wenn so ein Code doch weitergegebn wird - und glaub mir ich hab schon Code gesehn, bei dem ich bald vom Stuhl gefallen bin


----------



## DarkMo (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

naja klar, aber man weis doch bei seinen proggis eigentlich, wo die auf welche art und weise verwendung finden ^^ wenn ich beim auto im winter mit sommerreifen fahren will (scheiss autovergleiche immer, ich weis ><), dann is das in tiefliegenden regionen vllt nichmal problematisch, aber spätestens im gebirge wirste im besten falle zur lachnummer. das es auch im flachland ratsamer wäre ala "sicher ist sicher" ist klar, aber wenn mans halt ned brauch, weil man sich nur in der stadt aufhält (arbeit, einkaufen...), dann kann man ja die strassenverhältnisse "erahnen" ^^ wird ja nich die halbe stadt zugeeist sein un die andre is ok *g*


----------



## Bauer87 (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Spätestens, wenn man in nen Debugger (in meinem Fall gdb) geht, merkt man, das sein Return erwartet wird: kommt eine 0 zurück, ist alles in Ordnung, der gdb meldet „program exited normaly“. Wird das Programm aber mit einem anderen Return-Wert beendet, kommt „program quit (returned 1)“ oder entsprechendes.

Aber das ist ja im gegebenen Code ja richtig — also zurück zum eigentlichen Thema:

Bei uns compiliert der Code. Allerdings hast du ihn laut Kommentaren in einer Datei „dreieck.C“ gespeichert, was je nach Compiler zu Problemen führen kann. Für C++ verwendet man normalerweise als Dateiendung „cpp“ oder auch „cc“. Eventuell stolpert deine Umgebung sogar über den Großbuchstaben. (Ist der gcc unter Windows case-sensitive oder nicht?)


----------



## bingo88 (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Lass uns mal nicht über eine Zeile Code streiten  Außerdem wollte ich mal was von meinem Nerd-Wissen abgeben (wobei ich selbst erstaunt war, dass ich das wusste )
Mein Problem ist auch weniger, dass jemand das nutzt weil er weiß was er da tut, sondern weil das den Leuten so beigebracht wird.

Ja, das mit der Dateierweiterung kann ein Problem sein. Da ich aktuell nur auf Visual Studio Zugriff habe, kann ich das nicht ausprobieren (auch da ist MS lasch, du könntest die Datei auch *.foo nennen...)


----------



## Puepue (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Ich glaub' jetzt hast du ihn vertrieben ^^

Falls nicht: Sag uns doch mal ob das klappt, wenn du anstelle von .c  .cpp benutzt


----------



## bingo88 (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Ich hab auch nur Visual Studio auf dem PC hier. Müsste dann erst mal mein Macbook rauskramen... aber da kann ich mich atm nicht zu motivieren. Es ist ja nur ne Datei umzubenennen


----------



## Puepue (18. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Ich meinte eigentlich den Fragesteller der hat sich nämlich schon ne ganzeWeile nicht mehr gemeldet und deshalb meinte ich du hast ihn vertrieben ^^


----------



## bingo88 (19. März 2010)

*AW: Ständge C++ Fehlerausgabe bei int main()*

Watt? Wegen dem bissl Assembler?  Dabei hab ich doch noch garnicht angefangen gehabt


----------

