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

drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

11

11.07.2017, 00:41

Ich habe das Beispiel bei Wikipedia jetzt mal auf die einfachste Art repariert, die mir einfiel: static int zahl statt int zahl.

Hmm. Ich würde da noch einen Kommentar hinzufügen, der auf das "merkwürdige" Verhalten von statischen Variablen hinweist, dass diese nur 1x initialisiert werden (falls jemand auf die Idee kommt die Funktion mehrmals aufzurufen).

Im übrigen ist die einzige gängige Situation, die ich kenne, wo so etwas überhaupt Sinn macht sowieso nur das Meyers Singleton. Wundert mich, dass das noch niemand genannt hat. ^^

TigerClaw25

unregistriert

12

11.07.2017, 10:49

Ok, nochmal zum Verständnis:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
int *funktion(void) {
    int zahl = 3;
    int *zeiger = &zahl;
    return zeiger;           /* hier wird ein Zeiger zurückgegeben */
}
main() {
    int *zeiger;
    zeiger = funktion();
    printf("%d\n", *zeiger); /* gibt „3“ aus */
} 


Dieser Code in an sich falsch. Was mir aber als erstes auffällt ist die Deklaration und definition der Variablen "int zahl = 3;"

Danach übergebe ich die Adresse dem Zeiger und gebe diesen zurück. Danach wird die Funktion verlassen. Geht das überhaupt??? Nach meinem jetzigen wissen ist "Zahl" eine lokale variable. Rufe ich die Funktion erneut in der Main auf, zeigt der Zeiger auf eine neue Adresse. Deshalb werden oft statische Variablen verwendet, die zwar auch lokal erzeugt werden, aber ihre feste Adresse haben, ist das richtig?

Und zum Punkt vorher, der beantwortet wurde, verstehe ich nicht wieso es falsch ist, dass in der Main die zurück gegebene Adresse einem Zeiger zugeordnet wird wie in dem beispielcode hier in der Main. Das steht in sehr viele Büchern so

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

13

11.07.2017, 11:07

Danach übergebe ich die Adresse dem Zeiger und gebe diesen zurück. Danach wird die Funktion verlassen. Geht das überhaupt??? Nach meinem jetzigen wissen ist "Zahl" eine lokale variable. Rufe ich die Funktion erneut in der Main auf, zeigt der Zeiger auf eine neue Adresse. Deshalb werden oft statische Variablen verwendet, die zwar auch lokal erzeugt werden, aber ihre feste Adresse haben, ist das richtig?

Das "geht" schon, im Sinne von: Der Compiler hält dich nicht davon ab. Es erzeugt aber undefiniertes Verhalten, was man dringendst vermeiden sollte. Das Programm könnte abstürzen oder irgendeine beliebige Ausgabe erzeugen. Also ja, der Code ist "falsch". Aber deine Aussage "zeigt der Zeiger auf eine neue Adresse" ist falsch. Die Adresse bleibt dieselbe, aber ihr Inhalt ist nach dem Verlassen der Funktion undefiniert. Statische Variablen, die in einer Funktion angelegt werden, existieren außerhalb der Funktion. Sie bleiben also gültig, auch nachdem die Funktion verlassen wurden. Generell sollte man sie eher nicht benutzen, da es beispielsweise Probleme mit Multi-Threading geben kann, wenn mehrere Threads gleichzeitig lesend/schreibend auf die Variable zugreifen. Sie existiert nämlich nur genau einmal - egal, wie oft die Funktion aufgerufen wird.

Und zum Punkt vorher, der beantwortet wurde, verstehe ich nicht wieso es falsch ist, dass in der Main die zurück gegebene Adresse einem Zeiger zugeordnet wird wie in dem beispielcode hier in der Main. Das steht in sehr viele Büchern so

Das ist nicht falsch. Ob du den Rückgabewert der Funktion in einer Variablen speicherst oder nicht, ist in diesem Beispiel egal, da er nur einmal benutzt wird. In "vielen" Fällen gilt folgende Faustregel: Wenn du den Rückgabewert an mehreren Stellen benötigst, dann lege ihn in einer Variablen ab, um die Funktion nur ein einziges Mal aufrufen zu müssen. Wenn du ihn nur an einer einzigen Stelle benötigst, dann kannst du darauf verzichten. Das gilt nicht nur für Rückgabewerte von Funktionen, sondern für alle Ausdrücke, die "kompliziert" zu berechnen sind.
Also, das Beispiel hätte genauso gut so aussehen können:

C-/C++-Quelltext

1
printf("%d\n", *funktion());

TigerClaw25

unregistriert

14

11.07.2017, 15:39

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

using namespace std;

int *funktion() {
    int zahl = 3;
    int *zeiger = &zahl;
    return zeiger;           /* hier wird ein Zeiger zurückgegeben */
}
int main() {
    int *zeiger;
    zeiger = funktion();
    cout << zeiger << endl;        // Zeiger zeigt auf Adresse mit Inhalt "3"
    *zeiger = *zeiger + 10;    // Inhalt, auf das Zeiger zeigt, kann nicht um 10 addiert werden, da nicht definiert
     cout << *zeiger << endl;  
     cout << funktion();       // Gleiche (Zufall) oder neue Adresse Adresse durch lokale Variable und lokale Zeigervariable von Funktion, Wert wieder 3 
     
    system("Pause");
    return 0;
} 


Da hast du Recht. Wobei ein erneuter Aufruf der Funktion dazu führt, dass der zurückgegebene Zeiger unter Umständen auch eine neue Adresse beinhalten könnte, da erneut eine lokale Variable (und eine neue lokale Zeigervariable) erzeugt wird, die dann im Stack an einer anderen Adresse stehen könnte.

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

15

11.07.2017, 15:54

Der Kommentar // Zeiger zeigt auf Adresse mit Inhalt "3" stimmt nicht. Der Wert an der Adresse kann 3 sein, muss er aber nicht - es ist eben undefiniert. Dass die Adresse bei jedem Aufruf gleich ist, ist auch nicht gesagt. Schau dir mal folgendes Beispiel an:

C-/C++-Quelltext

1
2
3
4
5
6
7
cout << "1: " << funktion() << endl;
cout << "2: " << funktion() << endl;
{
    int x = 42;
    cout << "3: " << funktion() << endl << "nicht wichtig: " << x << endl;
}
cout << "4: " << funktion() << endl;

Bei mir(!) ergibt das im Debug-Modus folgende Ausgabe:

Quellcode

1
2
3
4
5
1: 004FF82C
2: 004FF82C
3: 004FF820
nicht wichtig: 42
4: 004FF82C

Die ersten beiden Adressen sind gleich, die dritte nicht mehr. Die vierte ist wieder wie die ersten beiden. Versuch mal herauszufinden, warum das so ist.
Und dann: Schalt mal von Debug auf Release um und vergleiche das Ergebnis.
Zu guter letzt: Kannst du mir auch erklären, warum auch im Debug-Modus die dritte Adresse plötzlich gleich bleibt, wenn man die Variable x nicht ausgibt (schon noch definieren, halt nur nicht ausgeben)?

TigerClaw25

unregistriert

16

11.07.2017, 16:43

Die Fragen kann ich leider nicht beantworten. Das sich die eine Adresse von den anderen unterscheidet, sehe ich. Eventuell liegt das daran, dass in der Funktion eine Variable, eine Zeigervariable und das return (erzeugt ja auch eine Kopie) existieren???

Durch dein Beispiel habe ich jedoch etwas anderes in Erfahrung bringen können. Wenn eine Funktion aufgerufen wird, wird die main-Funktion nicht direkt verlassen. Bisher bin ich ausgegangen, dass ich durch den Aufruf einer anderen externen Funktion die main-Funktion verlasse und die Variablen ihre Gültigkeit verlieren, das ist jedoch nicht der Fall. Auch habe ich durch die Übung bemerkt, dass Zeiger direkt mit einem String definiert werden können.

Statt

C-/C++-Quelltext

1
2
3
char array[30] = "Test";
char *ptr = nullptr;
ptr = array;

geht auch

C-/C++-Quelltext

1
char *ptr = "Test";


Hat mit deinem Übungsbeispiel nicht viel zutun, ist aber eine Erfahrung mehr :)
Apropos Zeiger und Arrays:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
char *funktion(char *text) {
    static char neu[100];
    char *zeiger;
    zeiger = neu;
    zeiger = text;
    return neu;           /* hier wird ein Zeiger zurückgegeben */
}
int main() {
    char string[100] = "tester";
    char *ptr = nullptr;
    ptr = string;
    ptr = funktion("Test");
    cout << ptr << endl; 
    system("Pause");
    return 0;
}


Das habe ich mir als Übung geschrieben. Leider funktioniert die Zeile " zeiger = text;" nicht. Warum auch immer, Es ist eine normale Zuweisung. Es funktioniert nur, wenn ich schreibe "while(*zeiger++ = *text++)".

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »TigerClaw25« (11.07.2017, 16:58)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

17

11.07.2017, 17:06

Was verstehst du unter "funktioniert nicht"? Da wird ein Zeiger einem anderen temporär zugewiesen. Erst zeigt "zeiger" in Zeile 4 auf "neu" und in Zeile 5 dann auf "text". Und in Zeile 7 stirbt "zeiger" dann komplett. Wenn du Text kopieren willst, also die Daten, auf die ein Zeiger zeigt, dann musst du sie natürlich auch kopieren, da reicht es nicht einfach ein paar Zeiger zu verbiegen.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

TigerClaw25

unregistriert

18

11.07.2017, 21:22

Das Beispiel ist etwas verwirrend. Aber prinzipiell sollte es funktioniert per Zuweisung ptr = string; auf das String zu zeigen, außer ist würde einzelnen Elemente ansprechen, dann bräuchte ich eine Schleife für den Durchlauf aller Elemente ...

Tobiking

1x Rätselkönig

  • Private Nachricht senden

19

11.07.2017, 23:22

Aber prinzipiell sollte es funktioniert per Zuweisung ptr = string; auf das String zu zeigen, außer ist würde einzelnen Elemente ansprechen, dann bräuchte ich eine Schleife für den Durchlauf aller Elemente ...

Wenn du auf einzelne Elemente zugreifen möchtest kannst du in dem Fall auch ptr[n] oder *(ptr+n) verwenden. Bei Zeigerarithmetik wird immer um die Größe des zu Grunde liegenden Typs verschoben, was in dem Fall genau dem Aufbau des Arrays entspricht, bei dem die einzelnen Werte direkt hintereinander im Speicher liegen.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

20

12.07.2017, 06:32

Das Beispiel ist etwas verwirrend. Aber prinzipiell sollte es funktioniert per Zuweisung ptr = string; auf das String zu zeigen, außer ist würde einzelnen Elemente ansprechen, dann bräuchte ich eine Schleife für den Durchlauf aller Elemente ...
Dann zeigt aber 'ptr' auf String und nicht 'ein_anderer_ptr', wie du es bei deinem Beispiel erwartet hast. Mal davon abgesehen, dass du da nur lokale Variablen änderst und die sind beim Verlassen der Funktion futsch. Erst zeigt 'zeiger' eben auf 'neu' und danach lässt du ihn auf 'text' zeigen. Und am Ende gibst du 'neu' zurück, der überhaupt nie irgendwie geändert wurde. Warum sollte der also auf irgendwas anderes zeigen als auf seine 100 Charakter (die übrigens im Release-Mode mit Zufallsdaten gefüllt sind)?
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Werbeanzeige