Hallo Zusammen, ich habe zu den Themen Call by Value/Stack und "char-Arrays" unter C++ jeweils eine Frage, für die es zwar andere Lösungen gibt, jedoch geht es mir um das Verständnis. 1. Wie genau funktioniert Call by Value, da in Büchern oft nicht exakt beschrieben wird, wie es funktioniert. Wir bei Aufruf einer Funktion der Wert des zu übergebenden Parameters an einer bestimmten Adresse im Stack gespeichert und von der Funktion selbst an einer anderen Adresse, wo eine Instanz erzeugt wird, der Wert sozusagen vom Stack kopiert? 2. Ich kann char-Arrays deklarieren und definieren: char Name[30] = "Test"; Getrennt funktioniert das aber nicht: char Name[30]; Name[] = "Test"; Woran liegt das? Mir ist klar, dass eine Zuweisung nur wie folgt möglich ist: Name[0] = 'T'; Name[1] = 'e'; Name[2] = 's'; Name[3] = 't'; Aber wieso ist eine Zuweisung des Strings "Test" direkt bei der Deklaration möglich, später jedoch nur, wenn die einzelnen Elemente [] angesprochen werden? Speicher wird mit oder ohne String bei der Deklaration reserviert, also spielt das zumindest keine Rolle bei der Zuweisung. Danke vorab für die Mühe.
Andersherum kann ich meine char Name[30] innerhalb einer Struktur nicht initialisieren:
1 | struct S_Spieler{ |
2 | int Energie; |
3 | int Punkte; |
4 | int xPosition; |
5 | int yPosition; |
6 | char Name[30]="Test"; //Nicht möglich, obwohl Speicherplatz reserviert wird |
7 | };
|
Michi schrieb: > 1. Wie genau funktioniert Call by Value, da in Büchern oft nicht exakt > beschrieben wird, wie es funktioniert. Das hängt von der Plattform und Calling Convention ab und kann daher nicht allgemein beantwortet werden, deswegen steht das da nicht. Es wird eine Kopie des Parameters an die Funktion übergeben, welche dann diese Kopie bearbeiten kann. Wenn der Parameter ein Pointer oder eine Referenz ist, wird eben der Pointer/Referenz kopiert, aber nicht das Ziel. Michi schrieb: > Wir bei Aufruf einer Funktion der > Wert des zu übergebenden Parameters an einer bestimmten Adresse im Stack > gespeichert Bei vielen Plattformen werden die ersten Parameter in Prozessor-Register kopiert und der Rest auf den Stack. Michi schrieb: > und von der Funktion selbst an einer anderen Adresse, wo > eine Instanz erzeugt wird, der Wert sozusagen vom Stack kopiert? Was für eine Instanz? Die Funktion nutzt die Werte vom Stack/Register direkt, ohne Kopie. Michi schrieb: > Woran liegt das? Mir ist klar, dass eine Zuweisung nur wie folgt möglich Man kann Arrays in C halt nicht direkt kopieren. Das wurde vermutlich so entschieden weil das eine Schleife beinhalten würde, und C Entwickler mögen keine versteckten aufwändigen Operationen. In C++ wurde das Verhalten auf den Built-In-Typen größtenteils übernommen, weswegen da auch so ist. Wenn du so etwas machen möchtest, nutze std::array oder std::vector oder std::string.
Michi schrieb: > struct S_Spieler{ > int Energie; > int Punkte; > int xPosition; > int yPosition; > char Name[30]="Test"; //Nicht möglich, obwohl Speicherplatz > reserviert wird > }; dann hast du nen ziemlich angestaubten C++ Compiler.
1 | struct Test { |
2 | char name[20] = "Default"; |
3 | int i = 5; |
4 | float f = 4.f; |
5 | |
6 | };
|
meiner übersetzt sowas ohne zu zucken Davon abgesehen, sieht das, was du da anstellst relativ wenig nach C++ aus. Da würde man
1 | std::string Name; |
benutzen. Da gibts auch gleich die passenden Konstruktoren für string-Literale;
char name[20] = "Default"; Eigentlich ist es nicht richtig, dass der Compiler es übersetzt, da eine Struktur nur Deklarationen enthalten sollte. Die Initalisierung sollte immer mit den jeweiligen Instanzen erfolgen. Und was Dr. Sommer schrieb: "Was für eine Instanz? Die Funktion nutzt die Werte vom Stack/Register direkt, ohne Kopie." Die Funktion nutzt zwar Werte, jedoch wird die Variable beim Aufruf nicht direkt übergeben sondern vielmehr eine Kopie, die nach dem Beenden der Funktion ihre Gültigkeit verliert.
Michi schrieb: > Andersherum kann ich meine char Name[30] innerhalb einer Struktur nicht > initialisieren:struct S_Spieler{ > int Energie; > int Punkte; > int xPosition; > int yPosition; > char Name[30]="Test"; //Nicht möglich, obwohl Speicherplatz > reserviert wird > }; hier wird kein Speicher reserviert. Das legt nur eine Struct an, es gibt noch keine passende Variable.
Michi schrieb: > Eigentlich ist es nicht richtig, dass der Compiler es übersetzt, da eine > Struktur nur Deklarationen enthalten sollte. Die Initalisierung sollte > immer mit den jeweiligen Instanzen erfolgen. Eine struct ist eine public class. In-class init ist ab C++11 möglich. > Und was Dr. Sommer schrieb: "Was für eine Instanz? Die Funktion nutzt > die Werte vom Stack/Register direkt, ohne Kopie." Das ist die Kopie. Michi schrieb: > Woran liegt das? C-style Array werden (überwiegend) wie Adressen behandelt. Dementsprechend ist call-by-value nicht möglich. Du kannst allerdings ein Array in eine struct einbetten und die struct by-value übergeben (siehe std::array in C++).
Michi schrieb: > char name[20] = "Default"; > > Eigentlich ist es nicht richtig, dass der Compiler es übersetzt, da eine > Struktur nur Deklarationen enthalten sollte. Die Initalisierung sollte > immer mit den jeweiligen Instanzen erfolgen. Natürlich erfolgt die Initialisierung bei der Instanzierung. Vorher ist ja kein Speicher da. Nur ist das die Moderne Variante in C++ Member zu initialisieren. Damit vermeidet man in jedem Konstruktor die selben Werte schreiben zu müssen, die Gefahr laufen, bei Änderung vergessen zu werden. Außerdem sind so die Defaultwerte schön sichtbar neben der Definition. Außerdem spart man sich so oft, überhaupt einen Default-Konstruktor schreiben zu müssen. Für primitive Typen gibts das seit c++11. Mit c++14 sind nochmal die Regeln, wann das geht erleichtert worden.
:
Bearbeitet durch User
Peter II schrieb: > S_Spieler Doch, wenn ich per sizeof(S_Spieler) Aufrufe, ist Speicher reserviert, obwohl noch keine Instanz erzeugt.
Michi schrieb: > Doch, wenn ich per sizeof(S_Spieler) Aufrufe, ist Speicher reserviert, > obwohl noch keine Instanz erzeugt. nein, siezeof liefert die größe, sagt aber überhaupt nicht ob Speicher reserviert ist.
Vlad T. schrieb: > Für primitive Typen gibts das seit c++11. Mit c++14 sind nochmal die > Regeln, wann das geht erleichtert worden. Ich nutze den Visual Studio Express 2012 Compiler. Daher wird das wohl noch nicht unterstützt und ich kann die Initialisierung erst nach der Deklaration einer Instanz durchführen. Ich habe dazu noch eine andere Frage. Wenn ich eine Variable einer Struktur über eine Funktion ändern möchte, ist es dann sinnvoll die komplette Struktur per Zeiger zu übergeben und dann in der FUnktion die entsprechende Variable per "STRUKTUR->Punkte=293 .... " zu ändern oder kann ich auch einfach nur diese Variable übergeben? Funktionieren tut beides. Aufruf: BerechnePunkte(&Spieler1, Bonus); Funktion: void BerechnePunkte(S_Spieler Spielertemp, int Bonus) ------------------ Das hier geht auch: Aufruf: BerechnePunkte(&Spieler1.Punkte, Bonus); Funktion: void BerechnePunkte(int *p_Punkte, int Bonus) ------------------- Denn wenn ich mir in der Mainfunktion eine Instanz erzeuge, besitzt die Strukturvariable "Punkte" bereits eine Adresse. Also kann ich die Parameterübergabe per Zeiger auch nur für diese eine Variable machen. Dennoch wird meistens die komplette Struktur per Zeiger übergeben, warum?
Michi schrieb: > Ich nutze den Visual Studio Express 2012 Compiler. dann nutze doch einen neueren. Wenn du dich sowieso in C++ einarbeitest, dann nutze doch wenigstens eine aktuelle Version davon. Michi schrieb: > Ich habe dazu noch eine andere Frage. Wenn ich eine Variable einer > Struktur über eine Funktion ändern möchte, ist es dann sinnvoll die > komplette Struktur per Zeiger zu übergeben und dann in der FUnktion die > entsprechende Variable per "STRUKTUR->Punkte=293 .... " zu ändern oder > kann ich auch einfach nur diese Variable übergeben? Funktionieren tut > beides. Das ist immer eine Frage des Kontextes. Wenn die Informationen, die du in der Funktion brauchst, alle auch ohne das Objekt da sind, könnte man auch über einen einfachen Rückkagebewert nachdenken. Zeiger verwendet man in c++ nicht ganz so oft (eigentlich nur, wenn man optionale by-referenze Argumente braucht) Ansonsten sollte man Referenzen benutzen. > void BerechnePunkte(S_Spieler Spielertemp, int Bonus) wird nicht funktionieren, oder ist S_Spieler ein Pointer? Falls ja: sowas ist schlechter Stil. Seperate Pointer-Typ-Definitionen vermüllen meist nur den Namensraum und machen den Code unübersichtlich, weil man beim Lesen das S_Spieler erst in Spieler* übersetzen muss.
1 | void BerechnePunkte(S_Spieler& Spielertemp, int Bonus) |
Man sollte vielleicht auch überlegen, zu wem diese Funktion gehört. Ist es vielleicht eine Memberfunktion von Spieler? dann sollte sie so aussehen:
1 | class Spieler{ |
2 | void BerechnePunkte(int Bonus) |
3 | // ...
|
4 | }
|
oder gehört sie eher in die "Game"-Klasse. Die kennt ja in der Regel enthaltenen Spieler. hier könnte der Parameter also ein Index, oder eine Name sein, über den die Game-Klasse die Spieler referenziert. Ich hab das Gefühl bei deinem derzeitigem Wissensstand wäre es sinnvoller, du suchst dir ein modernes C++ Tutorial und arbeitest das erst mal durch (zusammen mit einem aktuellerem Compiler, wie VS2015 oder VS2017). Vielleicht findest du auch ein nettes Tutorial über Objektorientiertes Design
:
Bearbeitet durch User
Ob der VSE2012 Compiler C++11 schon unterstützt, weiß ich nicht, aber selbst wenn, müsste man ihn erst per Commandline-Parameter darum bitten. Eine typische Aktion bei mir nach dem ersten Compile eines C++-Projekts mit Eclipse/GCC: -std=C++14 in die Projektsettings eintragen.
Carl D. schrieb: > Ob der VSE2012 Compiler C++11 schon unterstützt, weiß ich nicht, > aber > selbst wenn, müsste man ihn erst per Commandline-Parameter darum bitten. > Eine typische Aktion bei mir nach dem ersten Compile eines C++-Projekts > mit Eclipse/GCC: -std=C++14 in die Projektsettings eintragen. Nein, muss man nicht. So einen Schalter gibt es erst seit kurzem: https://blogs.msdn.microsoft.com/vcblog/2016/06/07/standards-version-switches-in-the-compiler/ Hier gibt es übrigens eine Übersicht der unterstützten C++-Features je nach Version: https://msdn.microsoft.com/en-us/library/hh567368(v=vs.140).aspx Non-static data member initializers gibt es erst ab VS 2013.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.