Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Brotkeks

Frischling

  • »Brotkeks« ist der Autor dieses Themas

Beiträge: 26

Beruf: Azubi/Student

  • Private Nachricht senden

1

09.09.2015, 13:18

Wann weist man Variablen einen Wert zu?

Hallo :)

Eine Frage, die mich jetzt seit Kapiteln begleitet ist, wann man einer Variabel denn nun einen Wert zuweist und wann nicht?
Manchmal hat man ja zum Beispiel einfach: int Punkte;
manchmal dann wieder int Punkte = 0;
und manchmal int Punkte=5;

Bin manchmal etwas aufgeschmissen, wenn ich selbst ein Programm schreiben möchte und rumprobieren will, dann aber nicht weiß, ob ich jetzt den Wert 0 definiere oder einfach gar nichts.

Liebe Grüße und danke euch schon einmal :)

birdfreeyahoo

Alter Hase

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

2

09.09.2015, 13:30

Um welche Sprache handelt es sich denn? Am besten immer mit einem Standardwert initialisieren wie z.B. 0, 0.f, false, etc. je nachdem was auch im Kontext Sinn macht.
Du kannst nie wissen, vorallem bei C++ was am Anfang drinsteht und undefiniertes Verhalten kann schnell mal böse werden.

DeKugelschieber

Community-Fossil

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

3

09.09.2015, 13:45

Ein paar Tipps:

* Variablen so spät wie möglich deklarieren, also erst wenn du sie wirklich brauchst
* wenn möglich, sofort einen Wert zuweisen
* in C/C++ (und noch diversen anderen Sprachen, das musst du dann selbst googlen), werden Variablen nicht initilisiert, da kann also bei einem int i; sonst was drinstehen (unsicher!)
* in Java und anderen modernern managed Sprachen werden Variablen vorinitialisiert, also ein Object obj; in Java hat dann den Wert null, ein int i; den Wert 0

Hier noch ein Bespiel:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
int i; // wenn C++ steht hier irgendwas, Java stünde hier 0

hierPassiertWas(); // besser wäre dann das int i; hier drunter zu deklarieren

if(foo > 100){
    i = 2;
}
else{
    i = 3;
}

// hier hat i auf jeden Fall einen der Werte
// da wäre es auch egal ob in i Schrott steht oder nicht bei der Deklaration

Hello_Kitty!

unregistriert

4

09.09.2015, 13:57

Als erste Grundregel: Wenn du einen Anfangswert sinnvoll bestimmen kannst, solltest du ihn zuweisen.

Die genauen Initialisierungsregeln in C++ können durchaus eine gewisse Komplexität erreichen, aber für die elementaren Datentypen wie int und float gilt, dass globale Variablen (außerhalb von Funktionen) mit 0 initialisiert werden. Lokale Variablen (innerhalb von Funktionen) werden nicht initialisiert und können beliebige Werte annehmen. Deshalb ist es hier besonders wichtig, dass du Anfangswerte bereitstellst.

Dieses Verhalten hat auch einen technischen Hintergrund: Wenn dein Programm zum ersten Mal startet, bekommst du vom Betriebssystem einen frischen Speicherbereich, der aus Sicherheitsgründen vollständig mit 0 initialisiert wurde (da könnte sonst ja alles mögliche drinstehen, u.a. auch Passwörter). Globale Variablen sind deshalb immer 0. Lokale Variablen befinden sich auf dem Stack und dieser wächst und schrumpft während der Programmlaufzeit dynamisch, dabei werden alte Werte einfach zurückgelassen und nicht mehr auf 0 zurückgesetzt. Deshalb kann es dir hier passieren, dass eine uninitialisierte, lokale Variable quasi beliebige Werte annimmt.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Hello_Kitty!« (09.09.2015, 17:36)


birdfreeyahoo

Alter Hase

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

5

09.09.2015, 15:08

aber für die elementaren Datentypen wie int und float gilt, dass Variablen auf dem Heap (globale Variablen, alles außerhalb von Funktionen) mit 0 initialisiert werden.


Meinst du mit Variablen außerhalb von Funktionen auch Klassenmember? Ist es bei denen nicht abhängig von wo die Instanz liegt (Pointer/lokale Variable) wo sie liegen?

cojo2015

Alter Hase

Beiträge: 516

Wohnort: bei mir zu Hause

Beruf: Schüler

  • Private Nachricht senden

6

09.09.2015, 15:41

Ich persönlich mag es, wenn man (fast) alle Variablen an einer Stelle deklariert bzw. definiert. (z.B. Bei Klassen -> deklaration immer untereinander und sortiert nach Datentyp und definiert im Konstruktor(falls es das Programm zulässt)). Hier ein kleines Beispiel:

Header:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Hier die ganzen #include's und evtl ein namespace

class Foo
{
public:
     Foo();
     ~Foo();

private:
     int m_iNum1;
     int m_iNum2;

     float m_fNum1;
     float m_fNum2;

};


Quelldatei:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
Foo::Foo()
{
     m_iNum1 = 0;
     m_iNum2 = 0;

     m_fNum1 = 0.f;
     m_fNum2 = 0.f;
}

// ...


Ich persönlich mag es halt strukturiert :)

Hello_Kitty!

unregistriert

7

09.09.2015, 16:14

@birdfreeyahoo
Mit der Formulierung wollte ich eigentlich nur dabei helfen den Unterschied zwischen lokalen und globalen Variablen erkennbarer zu machen, ich glaube Brotkeks ist noch nicht soweit großartig eigene Klassen zu implementieren. Also du hast recht, für Membervariablen gilt das so allgemein nicht, der Compiler erzeugt da ja auch oft automatisch zusätzlichen Code, um dem C++ Standard gerecht zu werden.

Für mich ist es schon eine Weile her, dass mich die genauen Details interessiert haben, aber eine Suche in der cppreference führt mich zur default initialization:

Zitat

This is the initialization performed when a variable is constructed with no initializer.

Default initialization is performed in three situations:
[...]
3) when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

The effects of default initialization are:
[...]
Otherwise [non class / non array], nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.
Also in dem von dir geschilderten Fall sind Membervariablen undefiniert, unabhängig davon ob das Objekt auf dem Stack oder Heap erzeugt wurde. Das sagt der Standard, auf einem PC dürften Membervariablen globaler Objekte trotzdem immer 0 sein - weil der Speicher wie schon erwähnt vom Betriebssystem genullt wird vor der Zuteilung zur Variablen kein Code ausgeführt wird, der an diese Stelle etwas schreiben könnte.

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

8

09.09.2015, 16:19

Ich persönlich mag es, wenn man (fast) alle Variablen an einer Stelle deklariert bzw. definiert. (z.B. Bei Klassen -> deklaration immer untereinander und sortiert nach Datentyp und definiert im Konstruktor(falls es das Programm zulässt)).

Bei einer Klasse ist es auch relativ egal wo du die Variablen definierst. Ob es nun zwischen den Methoden ist oder darüber oder darunter oder verteilt macht im Prinzip keinen Unterschied. Hier geht es ja im Thema um lokale Variablen. Ich denke alles andere wird der Threadersteller noch nicht gesehen haben.

Ich würde DeKugelschieber an sich zustimmen. Deklariere Variablen dann wenn du sie benötigst. Meistens ist es dann auch so dass du schon einen gewissen Startwert für die Variable haben möchtest. Es wurde ja schon mehrfach darauf hingewiesen dass nicht initialisierte Variablen, also die denen du selbst keinen Wert zugewiesen hast, allen möglichen Quatsch enthalten können. Einige Compiler fügen im Debugmodus Startwerte hinzu. Wenn du mit Visual Studio entwickelst und als Debug kompilierst hat eine Int-Variable normalerweise immer den Standardwert 0 auch wenn du ihr keinen zugewiesen hast. Kompilierst du das ganze aber irgendwann im Releasemodus dann ist das eben nicht mehr der Fall. Dann kann es dir unter Umständen auch passieren dass dein Programm abstürzt weil bestimmte Werte in Variablen nicht interpretiert werden können. Jetzt könnte man meinen solange du im Debugmodus kompilierst und entwickelst brauchst du deine Variablen nicht zu initialisieren. Davon würde ich dir aber abraten. Im Releasemodus wird der Code weiter optimiert und irgendwann wirst du das ganze vermutlich als Release übersetzen wollen. Wenn du dann deinen gesamten Code nach nicht initialisierten Variablen absuchen musst ist das ziemlich ätzend.

Also noch mal in kurz, leg Variablen möglichst erst dann an wenn du sie brauchst und wenn du keinen Wert hast den du darin speichern möchtest dann weise ihnen einfach einen Standardwert zu.

C-/C++-Quelltext

1
2
3
4
5
6
7
// hier soll etwas gezählt werden solange irgendeine Bedingung gilt (irgendeine Bedingung ist in diesem Fall nur ein Platzhalter, so würde das nicht übersetzt werden können.
int counter = 0;
while(irgendeine Bedingung)
{
  counter++;
}
std::cout << "Der Counter ist: " << counter << std::endl;


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int irgendeineEingabe = 1337;

int a = 0; // hier ist 0 einfach als Standardwert.

switch(irgendeineEingabe)
{
case 42:
  a = irgendeineFunktion1();
break;

case 19:
  a = irgendeineFunktion2();
break;

case 1337:
  a = irgendeineFunktion3();
break;

// hier geht es weiter
}

std::cout << "a hat den Wert: " << a << std::endl;

Die Variable a soll in diesem Fall erst in der Switch-Anweisung ihren eigentlichen Wert bekommen. Da wir nach Switch darauf zugreifen wollen müssen wir sie vorher deklarieren. Würden wir keinen Standardwert zuweisen und keiner der case-Fälle eintreten würde der Variable kein Wert zugewiesen. Dann kann es eben sein dass in a irgendwelche Bytes stehen und unter Umständen erzeugst du so Fehler in deinem Programm. In diesem Fall könnte man der Switch-Anweisung natürlich einfach noch ein default geben was aufgerufen wird wenn kein case-Fall eintritt. So etwas kann aber mal vergessen werden oder du änderst nachträglich etwas am Code und vergisst dass a vorher keinen Wert bekommt. Also versuch dir anzugewöhnen einfach immer einen Wert zuzuweisen.

Aber auch hier gilt learning by doing. Man kann sich da viele Gedanken drüber machen und wirklich weiter kommt man nicht. Versuch die Aufgaben im Buch zu lösen. Versuch selbst mit dem neugelernten zu experimentieren und irgendwann wird alles klarer. Je mehr du programmierst desto mehr festigt sich der Stoff.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Hello_Kitty!

unregistriert

9

09.09.2015, 17:01

Noch ein Nachtrag: Wenn ich von globalen Variablen gesprochen habe, meinte ich damit tatsächlich Variablen im static store. Wenn globale Variablen dynamisch, also auf dem Heap angelegt werden, verhalten sie sich nicht anders als Variablen auf dem Stack. Bei erstmaliger Nutzung sind sie normalerweise 0 (das ist aber auch nie garantiert), danach sind sie undefiniert. Meine erste Antwort habe ich diesbezüglich korrigiert.

Also noch mal in kurz, leg Variablen möglichst erst dann an wenn du sie brauchst und wenn du keinen Wert hast den du darin speichern möchtest dann weise ihnen einfach einen Standardwert zu.
Das sollte der Merksatz für diesen Thread sein, damit sind alle relevanten Fälle erledigt.

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

10

09.09.2015, 18:02

@Schorsch
Kleine Ergänzung:
Lokale Variablen werden in Visual Studio im Debug-Mode normalerweise mit hexadezimal 0xCC aufgefüllt. Ein Ausnullen würde den Fehler ja im Debugmodus eher noch mehr verstecken. Mindestens der 2015er Version ist standardmäßig im Debug-Mode zusätzlich auch noch ein Runtime Check aktiviert, da kann der Fehler eigentlich gar nicht mehr unbemerkt bleiben.
Genauere Infos gibt es hier:
https://msdn.microsoft.com/en-us/library/8wtf2dfz.aspx

In deinem zweiten Beispiel würde ich deshalb die Variante ohne Initialisierung bevorzugen. Anstatt dem unter Umständen nicht sinnvollen Wert 0 gäbe es dann nämlich im Debug Build einen Runtime-Fehler. Wenn man zum Beispiel an einer Stelle versehentlich einen Enumerationswert im switch vergisst, bliebe das sonst länger versteckt.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Spiele Programmierer« (10.09.2015, 02:25)


Werbeanzeige