Erste Schritte mit C ++

Download c++ eBook

Bemerkungen

Das 'Hello World'-Programm ist ein allgemeines Beispiel, das einfach zur Überprüfung der Anwesenheit von Compiler und Bibliothek verwendet werden kann. Es verwendet die C ++ - Standardbibliothek mit std::cout von <iostream> und hat nur eine zu kompilierende Datei, wodurch die Möglichkeit möglicher Benutzerfehler während der Kompilierung minimiert wird.


Das Kompilieren eines C ++ - Programms unterscheidet sich grundsätzlich zwischen Compilern und Betriebssystemen. Das Thema Kompilieren und Erstellen enthält Details zum Kompilieren von C ++ - Code auf verschiedenen Plattformen für verschiedene Compiler.

Versionen

Ausführung Standard Veröffentlichungsdatum
C ++ 98 ISO / IEC 14882: 1998 1998-09-01
C ++ 03 ISO / IEC 14882: 2003 2003-10-16
C ++ 11 ISO / IEC 14882: 2011 2011-09-01
C ++ 14 ISO / IEC 14882: 2014 2014-12-15
C ++ 17 TBD 2017-01-01
C ++ 20 TBD 2020-01-01

Hallo Welt

Dieses Programm druckt Hello World! zum Standardausgabestrom:

#include <iostream>

int main()
{
    std::cout << "Hello World!" << std::endl;
}
 

Sehen Sie live auf Coliru .

Analyse

Lassen Sie uns jeden Teil dieses Codes detailliert untersuchen:

  • #include <iostream> ist eine Präprozessoranweisung , die den Inhalt der Standard-C ++ - Headerdatei iostream .

    iostream ist eine Standard-Bibliothekskopfdatei , die Definitionen der Standard-Eingabe- und Ausgabeströme enthält. Diese Definitionen sind im std Namespace enthalten, der unten erläutert wird.

    Die Standard-E / A-Ströme bieten Programmen die Möglichkeit, Eingaben von einem externen System zu erhalten und an dieses auszugeben - normalerweise das Terminal.

  • int main() { ... } definiert eine neue Funktion namens main . Konventionell wird die main bei Ausführung des Programms aufgerufen. In einem C ++ - Programm darf nur eine main vorhanden sein, und es muss immer eine Zahl vom Typ int werden.

    Das int ist der Rückgabetyp der Funktion. Der von der main zurückgegebene Wert ist ein Beendigungscode.

    Konventionell wird ein Programm-Exit-Code von 0 oder EXIT_SUCCESS von einem System, das das Programm ausführt, als Erfolg interpretiert. Jeder andere Rückkehrcode ist mit einem Fehler verbunden.

    Wenn keine return - Anweisung vorhanden ist, die main (und damit das Programm selbst) liefert 0 standardmäßig. In diesem Beispiel müssen wir return 0; nicht explizit schreiben return 0; .

    Alle anderen Funktionen, mit Ausnahme derjenigen, die den void Typ zurückgeben, müssen explizit einen Wert entsprechend ihrem Rückgabetyp zurückgeben, sonst dürfen sie überhaupt nicht zurückgeben.

  • std::cout << "Hello World!" << std::endl; druckt "Hallo Welt!" zum Standardausgabestrom:

    • std ist ein Namespace , und :: ist der Operator für die Bereichsauflösung , der Lookups für Objekte nach Namen in einem Namespace ermöglicht.

      Es gibt viele Namespaces. Hier verwenden wir :: zu zeigen, dass wir cout aus dem std Namespace verwenden möchten. Weitere Informationen finden Sie unter Scope Resolution Operator - Microsoft-Dokumentation .

    • std::cout ist die Standardausgabe Objekt, in definierten iostream , und druckt es auf die Standardausgabe ( stdout ).

    • << ist in diesem Zusammenhang der Stream-Einfügungsoperator , der so genannt wird, weil er ein Objekt in das Stream- Objekt einfügt .

      Die Standardbibliothek definiert den Operator << , um Daten für bestimmte Datentypen in Ausgabeströme einzufügen. stream << content fügt content in den Stream ein und gibt denselben, aber aktualisierten Stream zurück. Dadurch können Stream-Einfügungen verkettet werden: std::cout << "Foo" << " Bar"; druckt "FooBar" auf die Konsole.

    • "Hello World!" ist ein Zeichenkettenliteral oder ein "Textliteral". Der Stream-Einfügeoperator für Zeichenkettenliterale wird in der Datei iostream definiert.

    • std::endl ist ein spezielles E / A-Stream-Manipulatorobjekt , das auch in der Datei iostream definiert ist. Durch Einfügen eines Manipulators in einen Stream wird der Status des Streams geändert.

      Der Stream-Manipulator std::endl führt zwei Dinge aus: Zuerst fügt er das Zeilenende-Zeichen ein und leert dann den Stream-Puffer, damit der Text auf der Konsole angezeigt wird. Dadurch wird sichergestellt, dass die in den Stream eingefügten Daten tatsächlich auf Ihrer Konsole angezeigt werden. (Stream-Daten werden normalerweise in einem Puffer gespeichert und dann in Batches "geleert", sofern Sie nicht sofort eine Spülung erzwingen.)

      Eine alternative Methode, die den Flush vermeidet, ist:

      std::cout << "Hello World!\n";
       

      Dabei ist \n die Escape-Zeichenfolge für das Zeilenvorschubzeichen.

    • Das Semikolon ( ; ) benachrichtigt den Compiler darüber, dass eine Anweisung beendet wurde. Alle C ++ - Anweisungen und Klassendefinitionen erfordern ein abschließendes Semikolon.

Bemerkungen

Ein Kommentar ist eine Möglichkeit, beliebigen Text in den Quellcode einzufügen, ohne dass der C ++ - Compiler ihn mit funktionaler Bedeutung interpretiert. Kommentare werden verwendet, um Einblick in das Design oder die Methode eines Programms zu geben.

Es gibt zwei Arten von Kommentaren in C ++:

Einzeilige Kommentare

Die doppelte Schrägstrichfolge // markiert den gesamten Text bis zu einer neuen Zeile als Kommentar:

int main()
{
   // This is a single-line comment.
   int a;  // this also is a single-line comment
   int i;  // this is another single-line comment
}
 

C-Style / Block-Kommentare

Mit der Sequenz /* wird der Beginn des Kommentarblocks und mit der Sequenz */ das Ende des Kommentars angegeben. Der gesamte Text zwischen den Start- und Endsequenzen wird als Kommentar interpretiert, auch wenn der Text ansonsten eine gültige C ++ - Syntax ist. Diese werden manchmal als "C-style" -Kommentare bezeichnet, da diese Kommentarsyntax von der C ++ - Vorgängersprache C übernommen wird:

int main()
{
   /* A block comment with the symbol /*
      Note that the compiler is not affected by the second /*
      however, once the end-block-comment symbol is reached,
      the comment ends.
   */
   int a;
}
 

In jedem Blockkommentar können Sie alles schreiben, was Sie möchten. Wenn der Compiler auf das Symbol */ stößt, wird der Blockkommentar abgebrochen:

void SomeFunction(/* argument 1 */ int a, /* argument 2 */ int b);
 

Das obige Beispiel ist gültiger C ++ - Code (und C-Code). Wenn Sie jedoch /* in einem Blockkommentar enthalten, kann dies bei einigen Compilern zu einer Warnung führen.

Block-Kommentare können auch innerhalb einer einzelnen Zeile beginnen und enden. Zum Beispiel:

int main()
{
   /*
    *  This is a block comment.
    */
   int a;
}
 

Bedeutung der Kommentare

Wie bei allen Programmiersprachen bieten Kommentare mehrere Vorteile:

  • Explizite Dokumentation des Codes, um das Lesen / Verwalten zu erleichtern
  • Erläuterung des Zweckes und der Funktionalität des Codes
  • Details zum Verlauf oder zur Begründung des Codes
  • Platzierung von Copyright / Lizenzen, Projektnotizen, besonderen Dank, Mitwirkenden, etc. direkt im Quellcode.

Kommentare haben jedoch auch ihre Nachteile:

  • Sie müssen beibehalten werden, um Änderungen im Code widerzuspiegeln
  • Exzessive Kommentare machen den Code weniger lesbar

Der Bedarf an Kommentaren kann reduziert werden, indem klarer, selbstdokumentierender Code geschrieben wird. Ein einfaches Beispiel ist die Verwendung von erklärenden Namen für Variablen, Funktionen und Typen. Das Ausrechnen von logisch zusammenhängenden Aufgaben in diskrete Funktionen geht damit Hand in Hand.

Kommentarmarken zum Deaktivieren von Code

Während der Entwicklung können Kommentare auch dazu verwendet werden, Teile des Codes schnell zu deaktivieren, ohne ihn zu löschen. Dies ist häufig für Test- oder Debugging-Zwecke hilfreich, eignet sich jedoch nur für temporäre Bearbeitungen. Dies wird oft als "Auskommentieren" bezeichnet.

In ähnlicher Weise ist es verpönt, alte Versionen eines Codes in einem Kommentar zu Referenzzwecken zu behalten, da dies Dateien stört und dabei wenig Wert bietet, verglichen mit der Erforschung der Geschichte des Codes über ein Versionssystem.

Funktion

Eine Funktion ist eine Codeeinheit, die eine Folge von Anweisungen darstellt.

Funktionen können Argumente oder Werte annehmen und einen einzelnen Wert zurückgeben (oder nicht). Um eine Funktion zu verwenden, wird ein Funktionsaufruf für Argumentwerte verwendet und die Verwendung des Funktionsaufrufs selbst wird durch seinen Rückgabewert ersetzt.

Jede Funktion verfügt über eine Typensignatur - die Typen ihrer Argumente und den Typ ihres Rückgabetyps.

Funktionen werden von den Konzepten der Prozedur und der mathematischen Funktion inspiriert.

  • Hinweis: C ++ - Funktionen sind im Wesentlichen Prozeduren und folgen nicht der genauen Definition oder den Regeln mathematischer Funktionen.

Funktionen dienen häufig dazu, eine bestimmte Aufgabe auszuführen. und kann von anderen Teilen eines Programms aufgerufen werden. Eine Funktion muss deklariert und definiert werden, bevor sie an anderer Stelle in einem Programm aufgerufen wird.

  • Hinweis: Beliebte Funktionsdefinitionen können in anderen enthaltenen Dateien versteckt sein (häufig zur Vereinfachung und Wiederverwendung in vielen Dateien). Dies ist eine übliche Verwendung von Header-Dateien.

Funktionserklärung

Eine Funktionsdeklaration gibt dem Compiler die Existenz einer Funktion mit ihrem Namen und ihrer Typensignatur an. Die Syntax lautet wie folgt:

int add2(int i); // The function is of the type (int) -> (int)
 

Im obigen Beispiel erklärt die Funktion int add2(int i) dem Compiler Folgendes:

  • Der Rückgabetyp ist int .
  • Der Name der Funktion lautet add2 .
  • Die Anzahl der Argumente für die Funktion ist 1:
    • Das erste Argument ist vom Typ int .
    • Das erste Argument wird im Inhalt der Funktion mit dem Namen i .

Der Argumentname ist optional. Die Deklaration für die Funktion könnte auch folgende sein:

int add2(int i);  // The compiler will note that add2 is a function (int) -> int
int add2(int j);  // As add2 already has a definition of (int) -> int, the compiler
                  // will regard this as an error.
 

Gemäß der Eindefinitionsregel kann eine Funktion mit einer bestimmten Typensignatur nur einmal in einer gesamten C ++ - Codebasis deklariert oder definiert werden, die für den C ++ - Compiler sichtbar ist. Das heißt, Funktionen mit einer bestimmten Typensignatur können nicht neu definiert werden - sie müssen nur einmal definiert werden. Folgendes ist daher kein gültiges C ++:

void do_something(); // The function takes no parameters, and does not return anything.
                     // Note that it can still affect variables it has access to.
 

Wenn eine Funktion nichts zurückgibt, wird ihr Rückgabetyp als void . Wenn keine Parameter erforderlich sind, sollte die Parameterliste leer sein.

#include <iostream>

int add2(int i);    // Declaration of add2

// Note: add2 is still missing a DEFINITION.
// Even though it doesn't appear directly in code,
// add2's definition may be LINKED in from another object file.

int main()
{
    std::cout << add2(2) << "\n";  // add2(2) will be evaluated at this point,
                                   // and the result is printed.
    return 0;  
}
 

Funktionsaufruf

Eine Funktion kann aufgerufen werden, nachdem sie deklariert wurde. Das folgende Programm ruft beispielsweise add2 mit dem Wert 2 innerhalb der Funktion von main :

int add2(int i)       // Data that is passed into (int i) will be referred to by the name i
{                     // while in the function's curly brackets or "scope."
                    
    int j = i + 2;    // Definition of a variable j as the value of i+2.
    return j;         // Returning or, in essence, substitution of j for a function call to
                      // add2.
}
 

Hier ist add2(2) die Syntax für einen Funktionsaufruf.

Funktionsdefinition

Eine Funktionsdefinition * ähnelt einer Deklaration, enthält jedoch auch den Code, der ausgeführt wird, wenn die Funktion innerhalb ihres Rumpfes aufgerufen wird.

Ein Beispiel für eine Funktionsdefinition für add2 könnte sein:

int add2(int i)           // Code contained in this definition will be evaluated
{                         // when add2() is called with one parameter.
    int j = i + 2;
    return j;
}

int add2(int i, int j)    // However, when add2() is called with two parameters, the
{                         // code from the initial declaration will be overloaded,
    int k = i + j + 2 ;   // and the code in this declaration will be evaluated
    return k;             // instead.
}
 

Funktionsüberladung

Sie können mehrere Funktionen mit demselben Namen, aber unterschiedlichen Parametern erstellen.

int multiply(int a, int b = 7); // b has default value of 7.
int multiply(int a, int b)
{
    return a * b;               // If multiply() is called with one parameter, the
}                               // value will be multiplied by the default, 7.
 

Beide Funktionen werden mit dem gleichen Namen add2 . Die eigentliche Funktion, die aufgerufen wird, hängt jedoch direkt von der Menge und dem Typ der Parameter im Aufruf ab. In den meisten Fällen kann der C ++ - Compiler berechnen, welche Funktion aufgerufen werden soll. In einigen Fällen muss der Typ explizit angegeben werden.

Standardparameter

Standardwerte für Funktionsparameter können nur in Funktionsdeklarationen angegeben werden.

int multiply(int a = 10, int b = 20); // This is legal 
int multiply(int a = 10, int b);      // This is illegal since int a is in the former
 

In diesem Beispiel kann multiply() mit einem oder zwei Parametern aufgerufen werden. Wenn nur ein Parameter angegeben wird, hat b den Standardwert 7. Standardargumente müssen in den letzten Argumenten der Funktion angegeben werden. Zum Beispiel:

3+3
 

Spezielle Funktionsaufrufe - Operatoren

Es gibt spezielle Funktionsaufrufe in C ++, die eine andere Syntax als name_of_function(value1, value2, value3) . Das häufigste Beispiel sind Operatoren.

Bestimmte spezielle Zeichenfolgen, die vom Compiler auf Funktionsaufrufe reduziert werden, z. B. ! , + , - , * , % und << und viele mehr. Diese Sonderzeichen stehen normalerweise im Zusammenhang mit der nicht-programmierenden Verwendung oder werden für Ästhetik verwendet (z. B. wird das Zeichen + sowohl in der C ++ - Programmierung als auch in der Mathematik als Zusatzsymbol erkannt).

C ++ behandelt diese Zeichenfolgen mit einer speziellen Syntax. Im Wesentlichen wird jedoch jedes Auftreten eines Operators auf einen Funktionsaufruf reduziert. Beispielsweise der folgende C ++ - Ausdruck:

operator+(3, 3)
 

entspricht dem folgenden Funktionsaufruf:

int add2(int); // Omitting the function arguments' name is also permitted.
 

Alle Namen der operator beginnen mit dem operator .

Während in C ++ unmittelbarem Vorgänger C die Namen der Operatorfunktionen nicht durch Angabe zusätzlicher Definitionen mit unterschiedlichen Typensignaturen unterschiedlichen Bedeutungen zugewiesen werden können, ist dies in C ++ gültig. Das "Ausblenden" zusätzlicher Funktionsdefinitionen unter einem eindeutigen Funktionsnamen wird in C ++ als Operatorüberladung bezeichnet und ist in C ++ eine relativ verbreitete, aber keine universelle Konvention.

Präprozessor

Der Präprozessor ist ein wichtiger Teil des Compilers.

Es bearbeitet den Quellcode, schneidet einige Bits heraus, ändert andere und fügt andere Dinge hinzu.

In Quelldateien können Präprozessoranweisungen eingefügt werden. Diese Anweisungen weisen den Präprozessor an, bestimmte Aktionen auszuführen. Eine Direktive beginnt mit einem # in einer neuen Zeile. Beispiel:

#define ZERO 0
 

Die erste Präprozessoranweisung, die Sie treffen werden, ist wahrscheinlich die

#include <iostream>
 

Richtlinie. Was sie tut , ist nimmt alle something und fügt es in der Datei , wenn die Richtlinie war. Das Hallo Weltprogramm beginnt mit der Zeile

#define something something_else
 

In dieser Zeile werden die Funktionen und Objekte hinzugefügt, mit denen Sie die Standardeingabe und -ausgabe verwenden können.

Die C-Sprache, die auch den Präprozessor verwendet, hat nicht so viele Header-Dateien wie die C ++ - Sprache, aber in C ++ können Sie alle C-Header-Dateien verwenden.


Die nächste wichtige Richtlinie ist wahrscheinlich die

#if something==true
//code
#else
//more code
#endif

#ifdef thing_that_you_want_to_know_if_is_defined
//code
#endif
 

Richtlinie. Dies teilt dem Präprozessor mit, dass er beim Durchlaufen der Datei jedes Vorkommen von something mit something_else ersetzen sollte. Es kann auch Dinge ähnlich wie Funktionen machen, aber das zählt wahrscheinlich als fortgeschrittenes C ++.

Die something_else wird nicht benötigt, aber wenn Sie something als nichts definieren, dann verschwinden außerhalb der Präprozessoranweisungen alle Vorkommen von something .

Dies ist aufgrund der Direktiven #if , #else und #ifdef tatsächlich hilfreich. Das Format für diese wäre folgendes:

#include <something>
 

Diese Anweisungen fügen den Code ein, der sich im True-Bit befindet, und löschen die False-Bits. Dies kann verwendet werden, um Codebits zu haben, die nur in bestimmten Betriebssystemen enthalten sind, ohne dass der gesamte Code neu geschrieben werden muss.

Der Standard-C ++ - Kompilierungsprozess

Ausführbarer C ++ - Programmcode wird normalerweise von einem Compiler erzeugt.

Ein Compiler ist ein Programm, das Code aus einer Programmiersprache in eine andere Form übersetzt, die für einen Computer (mehr) direkt ausführbar ist. Die Verwendung eines Compilers zum Übersetzen von Code wird als Kompilierung bezeichnet.

C ++ erbt die Form des Kompilierungsprozesses von seiner "übergeordneten" Sprache C. Nachfolgend finden Sie eine Liste mit den vier Hauptschritten der Kompilierung in C ++:

  1. Der C ++ - Präprozessor kopiert den Inhalt aller enthaltenen Header-Dateien in die Quellcodedatei, generiert Makrocode und ersetzt symbolische Konstanten, die mit #define definiert sind, durch ihre Werte.
  2. Die vom C ++ - Präprozessor erzeugte erweiterte Quellcodedatei wird in eine für die Plattform geeignete Assemblersprache kompiliert.
  3. Der vom Compiler generierte Assembler-Code wird zu einem geeigneten Objektcode für die Plattform zusammengestellt.
  4. Die vom Assembler generierte Objektcodedatei wird mit den Objektcodedateien für alle Bibliotheksfunktionen verknüpft, die zum Erstellen einer ausführbaren Datei verwendet werden.
  • Hinweis: Ein Teil des kompilierten Codes ist miteinander verknüpft, jedoch nicht zum Erstellen eines endgültigen Programms. Normalerweise kann dieser "verknüpfte" Code auch in ein Format gepackt werden, das von anderen Programmen verwendet werden kann. Dieses "Bündel von verpacktem, verwendbarem Code" bezeichnen C ++ - Programmierer als Bibliothek.

Viele C ++ - Compiler können bestimmte Teile des Kompilierungsprozesses zur Vereinfachung oder zur zusätzlichen Analyse zusammenführen oder deren Zusammenführung aufheben. Viele C ++ - Programmierer verwenden verschiedene Werkzeuge, aber alle Werkzeuge folgen im Allgemeinen diesem allgemeinen Prozess, wenn sie an der Erstellung eines Programms beteiligt sind.

Der Link unten erweitert diese Diskussion und bietet eine schöne Grafik, um zu helfen. [1]: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html

Sichtbarkeit von Funktionsprototypen und Deklarationen

In C ++ muss Code vor der Verwendung deklariert oder definiert werden. Das folgende Beispiel erzeugt beispielsweise einen Fehler bei der Kompilierung:

int main()
{
  foo(2); // error: foo is called, but has not yet been declared
}

void foo(int x) // this later definition is not known in main
{
}
 

Es gibt zwei Möglichkeiten, dies zu beheben: Setzen Sie entweder die Definition oder Deklaration von foo() vor seiner Verwendung in main() . Hier ist ein Beispiel:

void foo(int);  // Prototype declaration of foo, seen by main
                // Must specify return type, name, and argument list types
int main()
{
  foo(2); // OK: foo is known, called even though its body is not yet defined
}

void foo(int x) //Must match the prototype
{
    // Define body of foo here
}
 

Es ist jedoch auch möglich, die Funktion "vorwärts zu deklarieren", indem nur eine "Prototyp" -Deklaration vor ihrer Verwendung gesetzt wird und der Funktionskörper später definiert wird:

// foo.h
void foo(int); // prototype declaration
 

Der Prototyp muss den Rückgabetyp ( void ), den Namen der Funktion ( foo ) und die foo der Argumentliste ( int ) angeben. Die Namen der Argumente sind jedoch NICHT erforderlich .

Eine gängige Möglichkeit, dies in die Organisation von Quelldateien zu integrieren, besteht darin, eine Header-Datei mit allen Prototypdeklarationen zu erstellen:

// foo.cpp --> foo.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
void foo(int x) { } // foo's body definition
 

und dann die vollständige Definition an anderer Stelle angeben:

// main.cpp --> main.o
#include "foo.h" // foo's prototype declaration is "hidden" in here
int main() { foo(2); } // foo is valid to call because its prototype declaration was beforehand.
// the prototype and body definitions of foo are linked through the object files
 

und verknüpfen foo.o nach dem Kompilieren die entsprechende Objektdatei foo.o mit der kompilierten Objektdatei, in der sie in der Verknüpfungsphase verwendet wird, main.o :

void foo(int x) {}  //Declare the foo function and body first

int main()
{
  foo(2); // OK: foo is completely defined beforehand, so it can be called here.
}
 

Ein „nicht aufgelöstes externes Symbol“ -Fehler auftritt , wenn der Funktionsprototyp und Anruf existieren, aber der Funktionskörper ist nicht definiert. Das Auflösen kann schwieriger sein, da der Compiler den Fehler erst im letzten Verknüpfungsschritt meldet und nicht weiß, in welche Zeile im Code gesprungen werden soll, um den Fehler anzuzeigen.

Stats

9370 Contributors: 111
Wednesday, August 2, 2017
Lizenziert unter: CC-BY-SA

Nicht angeschlossen an Stack Overflow
Rip Tutorial: info@zzzprojects.com

EBook herunterladen