C++ | Wenn IF-Anweisung klappt schließt sich das Programm

DeThPy

Grünschnabel
Guten Tag,
erstmal Hallo an alle, das ist mein erster Post hier! :)

Zumindest ich habe eine If-Anweisung gemacht, dazu mehrere Strings und eine Eingabe.
Wenn der eingegebene String (commandEingabe), einer der anderen entspricht dann führe aus.
Alles soweit gut, allerdings gebe ich etwas was passt, sehe ich wie diese das ganze ausgibt was ich angegeben habe, allerdings schließt sich das ganze Programm danach.
Hier mal das ganze:

C++:
void commandAbfrage();
int beendenProgramm();

int main()
{
    commandAbfrage();
}

void commandAbfrage()
{
    // Befehle die man eingaben kann
    string plusCmd = "plus";
    string plusCmdBeschreibung = "Einfaches Plus rechnen.";

    string helpCmd = "help";
    string helpCmdBeschreibung = "Gibt Hilfe und zeigt verfügbare Commands an.";

    string beenden = "beenden";
    string beendenBeschreibung = "Beendet dieses Programm.";
    string commandEingabe;

    cout << "Geben Sie einen Command ein: ";
    cin >> commandEingabe;
    cout << endl;

    if (commandEingabe == helpCmd)
    {
        cout << "====== Help ======";
        cout << endl;
        cout << "'" + plusCmd + "':" + plusCmdBeschreibung << endl;
        cout << "'" + helpCmd + "':" + helpCmdBeschreibung << endl;
        cout << "'" + beenden + "':" + beendenBeschreibung << endl;
        cout << endl;
        cout << "====== Help ======";
    }
    else if (commandEingabe == plusCmd)
    {
        cout << "hallo";
    }
    else if (commandEingabe == beenden)
    {
        beendenProgramm();
    }
    else
    {
        cout << "Eingegebener Command kann nicht gefunden werden.";
        cout << endl;
        cout << endl;
        commandAbfrage();
    }
}

int beendenProgramm()
{
    return 0;
}

Freue mich auf eine Antwort :)
 
Hallo DeThPy

Wenn eine deiner Kommandoabfragen zutrifft wird die entsprechende Aktion ausgeführt, die Funktion commandAbfrage kommt am Ende an, main kommt am Ende an, das Programm ist fertig.

Eine Alternative wäre es:
C++:
#include <iostream>

bool commandAbfrage();

int main()
{
    while(commandAbfrage());
}

bool commandAbfrage()
{
    // Befehle die man eingaben kann
    std::string plusCmd = "plus";
    std::string plusCmdBeschreibung = "Einfaches Plus rechnen.";

    std::string helpCmd = "help";
    std::string helpCmdBeschreibung = "Gibt Hilfe und zeigt verfügbare Commands an.";

    std::string beenden = "beenden";
    std::string beendenBeschreibung = "Beendet dieses Programm.";
    std::string commandEingabe;

    std::cout << "Geben Sie einen Command ein: ";
    std::cin >> commandEingabe;
    std::cout << std::endl;

    if (commandEingabe == helpCmd)
    {
        std::cout << "====== Help ======";
        std::cout << std::endl;
        std::cout << "'" + plusCmd + "':" + plusCmdBeschreibung << std::endl;
        std::cout << "'" + helpCmd + "':" + helpCmdBeschreibung << std::endl;
        std::cout << "'" + beenden + "':" + beendenBeschreibung << std::endl;
        std::cout << std::endl;
        std::cout << "====== Help ======";
    }
    else if (commandEingabe == plusCmd)
    {
        std::cout << "hallo";
    }
    else if (commandEingabe == beenden)
    {
        return false;
    }
    else
    {
        std::cout << "Eingegebener Command kann nicht gefunden werden.";
        std::cout << std::endl;
        std::cout << std::endl;
    }

    return true;
}

Viele Grüsse
Cromon
 
Ich bin zur Zeit noch sehr an C# gewöhnt und möchte umsteigen, aber gut Danke @Cromon! :)
(Muss man Main immer am Ende schreiben, man kann doch "Prototypen" benutzen, oder nicht?)
 
Hallo DeThPy

Ja, das generelle Prinzip ist gleich wie in C#: Wenn main() fertig ist, ist das Programm fertig.

Und ja, Prototypen kann und sollte man benutzen, manchmal ist es sogar so, dass man selbiges muss.

Viele Grüsse
Cromon
 
Bitte nicht :D
"void" main ist explizit nicht erlaubt.

Und zumindest bei neuen Standardversionen (oder auch allen)
ist der Returnwert implizit 0, wenn nichts angegeben wird.
 
Zuletzt bearbeitet:
Die Standarddefinition

... ist
N3690 §3.6.1 hat gesagt.:
A program shall contain a global function called main, which is the designated start of the program. It is implementation-defined whether a program in a freestanding environment is required to define a main function. [ Note: In a freestanding environment, start-up and termination is implementation-defined; startup contains the execution of constructors for objects of namespace scope with static storage duration; termination contains the execution of destructors for objects with static storage duration. — end note ]

An implementation shall not predefine the main function. This function shall not be overloaded. It shall have a return type of type int, but otherwise its type is implementation-defined. All implementations shall allow both
— a function of () returning int and
— a function of (int, pointer to pointer to char) returning int
 
"void" main ist explizit nicht erlaubt.
Ah ja. Süsses kleines MSVC++ :)

Und zumindest bei neuen Standardversionen (oder auch allen)
ist der Returnwert implizit 0, wenn nichts angegeben wird.
Tatsächlich, eine Ausnahmeregelung für die main() :eek:. Soweit ich gesehen habe, war das nicht immer so, sodass der Wert einfach undefiniert war. Aber schon C99 scheint das zu haben.

Dies verwirrt aber auch stark: Der ASM-Code ist wird dadurch nicht konsistent übersetzt: Für keine int-Funktion ausser der main() ist eine implizite Rückgabe erlaubt. Grund: Das EAX-Register muss gefüllt werden. "return" ist eigentlich nichts anderes als:
Code:
(013D179E  xor         eax,eax); Nur bei Rückgabe 0
(mov eax, Wert; Wert ist hierbei der Rückgabewert (hinter "return")); Nur bei nicht-void-Funktionen
} //Ende der Funktion
013D17A0  pop         edi
013D17A1  pop         esi
013D17A2  pop         ebx
013D17A3  mov         esp,ebp
013D17A5  pop         ebp
013D17A6  ret [Anzahl Bytes der Parameter]; (Nur unter _stdcall. _cdecl lässt den caller aufräumen)
"void" heisst ja gerade, dass "nichts" zurückkommt bzw. das EAX-Register nicht beachtet werden soll.
In diesem Falle ist MSVC++ sicher falsch: Denn void müsste bedeuten, dass eben nichts mit dem EAX-Register gemacht wird, was schlicht nicht zutrifft.
Auf der anderen Seite ist eine Funktion mit Rückgabetyp "int" falsch (und schmeisst einen Compiler Error), wenn sie eben _keinen_ Wert zurückgibt.

Interessanter wird es bei einer Funktion, bei der nicht alle "Steuerelementpfade" einen Wert zurückgeben:
C++:
int testInt(char a, char b)
{
    int c = a + b;
    if (c < 0)
        return 0;
}
Hier sieht das Disassembly so aus:
Code:
int testInt(char a, char b)
{
00231780  push        ebp
00231781  mov         ebp,esp
00231783  sub         esp,0CCh
00231789  push        ebx
0023178A  push        esi
0023178B  push        edi
0023178C  lea         edi,[ebp-0CCh]
00231792  mov         ecx,33h
00231797  mov         eax,0CCCCCCCCh
0023179C  rep stos    dword ptr es:[edi]
    int c = a + b;
0023179E  movsx       eax,byte ptr [a]  ;eax ist nun a
002317A2  movsx       ecx,byte ptr [b]
002317A6  add         eax,ecx  ;add speichert das Ergebnis in EAX
002317A8  mov         dword ptr [ c],eax
    if (c < 0)
002317AB  jns         testInt+2Fh (02317AFh)  ;Überbrückt das folgende XOR
        return 0;
002317AD  xor         eax,eax
}
002317AF  pop         edi
002317B0  pop         esi
002317B1  pop         ebx
002317B2  mov         esp,ebp
002317B4  pop         ebp
002317B5  ret

Entsprechend ergibt der folgende Code einen Wert in testEAX von 13.
C++:
int testEAX;
    testInt(10, 3);
    _asm
    {
        mov dword ptr [testEAX], eax
    }
return 1;

Ist es gesucht? Sicherlich. Aber das zeigt gerade den Vorteil von void: Dieser Rückgabetyp lässt EAX in Ruhe. Wenn man also saumässig performanten Code schreiben will und mit void eine mov-Operation sparen kann, warum nicht? Und ja: Natürlich kann ich alles in ASM schreiben, um die maximale Performance zu haben (oder auch nicht, je nachdem :) ). Aber: C sollte nicht mehr machen, als ASM zu abstrahieren, gewissermassen Makros zu definieren.
So, und jetzt zur alles entscheidenden Frage: Was passiert, wenn ich mithilfe des "Steuerelementpfades" eine Überbrückung des xor eax, eax zu erzwingen versuche?

Code:
int main()
{
;Normale Initialisierung
    volatile int one = 1;
0006143E  mov         dword ptr [one],1
    volatile int two = 2;
00061445  mov         dword ptr [two],2
    if (one == two)
0006144C  mov         eax,dword ptr [two]
0006144F  cmp         dword ptr [one],eax
00061452  jne         main+3Bh (06145Bh)
    {
        return 10;
00061454  mov         eax,0Ah
00061459  jmp         main+41h (061461h)
    }
    else
    {

    }
}
0006145B  jmp         main+3Fh (06145Fh)  ;Hier wird getrickst, um doch noch auf eine 0 zu kommen
0006145D  jmp         main+41h (061461h)  ;Hier wird dafür gesorgt, dass bestehende Rückgabewerte nicht überschrieben werden (Vorhergehende returns _müssen_ auf die Adresse 6145D oder 61461 jumpen)
0006145F  xor         eax,eax
00061461  pop         edi
00061462  pop         esi
00061463  pop         ebx
00061464  mov         esp,ebp
00061466  pop         ebp
00061467  ret
Dasselbe, aber mit explizitem Return:
Code:
int main()
{
;Wieder Initialisierung
    volatile int one = 1;
0089143E  mov         dword ptr [one],1
    volatile int two = 2;
00891445  mov         dword ptr [two],2
    if (one == two)
0089144C  mov         eax,dword ptr [two]
0089144F  cmp         dword ptr [one],eax
00891452  jne         main+3Bh (089145Bh)
    {
        return 10;
00891454  mov         eax,0Ah
00891459  jmp         main+3Dh (089145Dh)
    }
    else
    {

    }
    return 0;
0089145B  xor         eax,eax
}
0089145D  pop         edi
0089145E  pop         esi
0089145F  pop         ebx
00891460  mov         esp,ebp
00891462  pop         ebp
00891463  ret
Es werden 2 Instruktionen weniger benötigt, sieht sauberer aus und braucht keine Extrawurst.

Nun möchte ich dennoch einen undefinierten Wert erzeugen.
C++:
int main()
{
    volatile int one = 1;
    volatile int two = 2;

    int ret = 100;

    if (one == two)
    {
        return 10;
    }
    else
    {
        _asm
        {
            mov eax, dword ptr[ret]; Leicht zu erkennen
            ;Hoffentlich haut mir dieser Kaese nicht das System um die Ohren...
            pop         edi
            pop         esi
            pop         ebx
            mov         esp, ebp
            pop         ebp
            ret
        }
    }
    return 0;
}
Uuuuuuuund:
Code:
Das Programm "[4204] Projekt1.exe" wurde mit Code 100 (0x64) beendet.
Tadaaaa!

Ok, wieder mal einen Nachmittag lang rumgespielt :)

Ganz kleinlaut muss ich mich dennoch für Falschinformation entschuldigen:oops:

Ich denke, man kann daraus 3 Lehren ziehen:
1. Solcher Syntaktischer Zucker gehört verboten. 2 Instruktionen mögen nicht viel sein, und der Fall hier ist konstruiert (der Code Cromons ist identisch mit einem, der ein "return 0" zusätzlich hat), aber es kann vorkommen und ist unnötige Verschwendung. "return 0" tippt man in vielleicht 1.5 Sekunden beim Erstellen des Projektes und gut ist.
2. MSVC++ hat seltsame Definitionen.
3. Wer auch immer C standardisiert, mag ASM nicht.

Kleiner Funfact zum Schluss: Man kann durch die ASM-Eigenschaften auch problemlos einen char als Rückgabewert angeben. Dann wird aber entsprechend nur AL gesetzt. Was sich da wieder für Möglichkeiten auftun... :)

Gruss
cwriter
 
Zuletzt bearbeitet:
VS unterstützt wohl void weil das in C soweit auch ok ist. Dort sieht der Standard lediglich den Namen "main" vor.

N1570 §5.1.2.2.1 hat gesagt.:
The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters: int main(void) { /* ... */ } or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared): int main(int argc, char *argv[]) { /* ... */ } or equivalent;10) or in some other implementation-defined manner.

Siehe "or in some other implementation-defined manner."

Deshalb dann auch:
N1570 $5.1.2.2.3 hat gesagt.:
If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument;11) reaching the } that terminates the main function returns a value of 0. If the return type is not compatible with int, the termination status returned to the host environment is unspecified

Die meisten der Mikrooptimierungen, die sich auf ASM-Code berufen funktionieren meistens nur im Debugmodus, im Releasemodus funkt dir der Optimierer in Echtweltsituationen viel zu stark dazwischen. Die Signatur einer Funktion sollte nicht durch Optimierungen bestimmt werden sondern durch die durch die Architektur gegebenen Kriterien.
 
@cwriter

:eek: Nette Beitragsgröße :D
Ah ja. Süsses kleines MSVC++ :)
Muss gerade wieder an unsere PN-Unterhaltung wegen VS denken :)
Dies verwirrt aber auch stark: Der ASM-Code ist wird dadurch nicht konsistent übersetzt:
Naja, main ist ja auch so, ohne Asm, nicht exakt wie andere Funktionen.
Warum sollte es also 100% gleich behandelt werden?
In diesem Falle ist MSVC++ sicher falsch: Denn void müsste bedeuten, dass eben nichts mit dem EAX-Register gemacht wird, was schlicht nicht zutrifft. Auf der anderen Seite ist eine Funktion mit Rückgabetyp "int" falsch (und schmeisst einen Compiler Error), wenn sie eben _keinen_ Wert zurückgibt:
VS erlaubt zwar void als main-Returnwert, aber nach Programmende kann die aufrufende Stelle trotzdem einen Returnwert abfragen (auf der Seite ist alles wie es sein soll, Returnwert existiert immer). MS weiß einfach selber nicht was es will ... Und dass void erlaubt ist, und die Implizit-0-Sache nicht funktioniert, das sind einfach zwei der vielen vielen Eigenheiten, die MS glaubt haben zu müssen.
[ Andererseits schaffen sie es ja nicht einmal, C99 endlich voll zu unterstützen. Immerhin wird daran gearbeitet, 16 Jahre später... }

Zum weiteren Post (sorry wenn ich nicht alles konkret zitiere und darauf eingehe):
Mir ist leider noch nicht ganz klar worauf die hinauswillst :/
VS erzeugt teilweise unnötige Instruktionen, auch bei absichtlich falschem Code, bei dem sowieso alles Mögliche passieren kann. Ja gut (?) Aus Performancegründen Returnwerte weglassen und void nehmen geht schlecht, wenn man die Returnwerte braucht. Ob EAX bei void in Ruhe gelassen wird hängt auch sehr vom Compiler ab, garantiert ist es nicht.
(aber auf jeden Fall ist es eine schöne Beschäftigung, auf der Ebene herumzuspielen :D)

Zu Lehre 3: Dass Asm im C-Standard als nicht-existent betrachtet wird (so wie Computer usw., wenn ich mich richtig erinnere), dürfte einer der Gründe für die weite Verbreitung sein :) = Dass es eben sehr wenig Vorgaben an Gerät und Umgebung macht und damit so ziemlich überall einsatzfähig ist.
 

Neue Beiträge

Zurück