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

1

12.04.2013, 15:03

Doppelter Pointer in manchen Programmen

Hallo Leute,

ich sehe schonmal öfter, dass manche C++ Programme doppelte Pointer benutzen.
Also z.B.:

C-/C++-Quelltext

1
void SDL_WM_GetCaption(char **titel, char **icon_titel);


Wofür ist denn das gut?
Was bringt einem dieser doppelte Pointer?

Da bräuchte ich von euch ein bisschen Erklärungsbedarf.

Mit freundlichen Grüßen

Teizakk

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Teizakk« (12.04.2013, 15:13)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

12.04.2013, 15:21

Das ist ein Zeiger auf einen Zeiger.
Du gibst der Funktion SDL_WM_GetCaption zwei Zeiger auf char-Zeiger, damit sie sie "ausfüllt".
Genau wie man einer Funktion einen normalen Zeiger geben würde, damit sie die Variable, auf die er zeigt, "ausfüllt". Nur dass es hier eben ein Zeiger ist, der ausgefüllt werden soll und nicht z.B. ein int.

3

12.04.2013, 16:59

Aber einen wirklichen Sinn hat das Ganze nicht, oder? Ich meine, man könnte das ganze ja auch machen, wenn man nur einen einfachen Zeiger übergibt.

simbad

unregistriert

4

12.04.2013, 17:28

char ** sind eine andere Schreibweise für ein array of strings ohne array größe.
Man konnte das früher auch als

C-/C++-Quelltext

1
char *bla[];

Schreiben. Ich weiß nicht, ob da der Standard mittlerweile anders drüber denkt. Bei meinem Beispiel ist der Name bla ein char**.

Aber Achtung. char** und char*[] sind unterschiedliche Dinge, genauso wie char* und char[] unterschiedich sind.
Bei char** wird häufig das Ende mit einem NULL-Pointer markiert, so z.B. bei

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
main(int argc, char**argv) {

//
// du kannst den Inhalt von argv mit einem index ablaufen

for (int i=0;i<argc;++i) {
    cout << (*(argv+i)) << "\n";
}

//
//  Oder auf den letzten Eintrag warten

for (;(*argv) != 0;++argv) {
    cout << (*argv) << "\n";
}


Der Sinn darin ist, das char** auf ein array von Strings verweist ohne das ich die Länge Wissen muss.

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

5

12.04.2013, 17:38

Eben nicht. Du willst die Adresse des Zeigers haben und nicht die Adresse des Objektes. Wenn du folgendes hast:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int *internalP;

void Start()
{
    internalP = new int[1];
    (*internalPointer) = 3;
}

void GetSomePointer(int *p)
{
    p = internalP;
}

void DoSomething()
{
    delete internalP;
    int *newPointer = new int[1];
    (*newPointer) = 6;
    internalPointer = newPointer;
}

Mein C++ ist ziemlich eingerostet. Ich hoffe der Code ist soweit richtig und verständlich. Jetzt stell dir vor, dein Programm ruft Start() auf. InternalP zeigt nun auf einen Bereich an welchem der Int-Wert 3 gespeichert ist. Jetzt willst du von außen den Zeiger holen und rufst GetSomePointer(int *p) auf. Dein Zeiger wird nun auf internalP gesetzt und zeigt somit auf den selben Speicherbereich. Jetzt wird irgendwann DoSomething() aufgerufen und internalP umgesetzt. Er zeigt nun auf einen anderen Speicherbereich, in welchem der Int-Wert 6 abgespeichert ist. Der alte Zeiger wird vorher freigegeben. Das ist aber der Zeiger, welchen wir uns vorher geholt hatten. Das heißt unser über p geholter Zeiger zeigt nun auf einen nicht definierten Speicherbereich.
Würde die Funktion GetSomePointer nun aber einen Zeiger auf einen Zeiger auf ein int nehmen, hätten wir was wir wollen.
Ein anderes Beispiel ist eine Funktion zum tauschen.

Quellcode

1
2
3
4
5
6
void swap(val1, val2)
{
    temp = val1;
    val1 = val2;
    val2 = temp;
}

Damit die Funktion funktioniert müssten die Parameter Zeiger sein. Würdest du nun aber zum Beispiel gern Zeiger auf Integer oder so tauschen wollen, müssten die Parameter Zeiger auf Zeiger auf Integer sein. Sollte klar sein.

edit: Ich beziehe mich auf das vor simbads Antwort.
„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.“

simbad

unregistriert

6

12.04.2013, 19:24

Eben nicht. Du willst die Adresse des Zeigers haben und nicht die Adresse des Objektes. Wenn du folgendes hast:

edit: Ich beziehe mich auf das vor simbads Antwort.

Na ein Glück. Ich hab das Post gelesen und dachte mir, was zum Teufel erzählt er da.....hatte schon Angst Müll erzählt zu haben. Das kommt ja auch manchmal vor.

Für die SDL_-Funktion ist sicherlich entscheidend, das der Speicherbereich, der dort als Ergebnis geliefert wird in der Funktion alloziiert wird. Damit also die Adresse dieses Speicherbereichs zurück geliefert werden kann, was man auch als return Wert hätte tun können, aber sicherlich nicht der Philosphie der SDL Entwickler entsprach, muss man eben die Addresse bekannt machen, in der der Pointer eingetragen wird, das ist alles.

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

7

12.04.2013, 22:00

Da ich das vor Kurzem schon mal Ausfuehrlicher erklaert habe, ein kleiner Link dorthin:
Laden komplexer Objekte mit vielen Vertices aus *.obj-Dateien (DirectX 9)

simbad Erklaerung ist nicht ganz falsch, aber IMHO doch sehr irrefuehrend. Im reinem C++ gibt es nicht wirklich Arrays. Man kann Pointer und Pointerarithemtik aber so aehnlich benutzen. Dazu muss man sich erstmal darauf einigen, das wir im Speicher hintereinander liegende Werte als ein "Array" ansehen wollen. Nur in dieser Interpretation ist ein char** ein Array von char-"Arrays", was aber noch nichts darueber aussagt, ob man mit dieser Interpretation die Speicherwerte sinnvoll verstehen kann.

simbad

unregistriert

8

12.04.2013, 22:32

Ohne es näher zu erklären sprichst du das Problem von char* und char[] an.
Speziell in C gibt es da folgende Situation recht häufig:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//
//  Hier wird tatsächlich ein Bereich im Speicher definiert, in den hinein das array angelegt wird. 
//  Das Symbol text wird direkt mit dem Array verbunden. Beim Zugriff wird eine einfach indirektion verwendet.
char text[]={"Ich bin ein Text"};
//
//  Hier wird wieder ein Bereich im speicher definiert, in den der Text abgelegt wird, aber zusätzlich eine Variable 
//  erzeugt, die die Adresse des Bereichs enthält. Man halt also eine doppelte indirektion.
char *moretext="Ich bin noch mehr text";
//
// Das Problem ist jetzt, das der Name der beiden Variablen jeweils die Addresse des Text ist, der Compiler aber 
//  unterschiedlichen Code produziert um darauf zuzugreifen. Man kann den Namen der Variablen identisch verwenden.
//
//  Wenn man in einem Modul ein Array so definiert
int Nummern[]={1,2,3,4};
//
//  Und die Nummern im anderen so importiert.
extern int *Nummern;
//
//  Dann geht das voll daneben, weil in dem Modul in dem Nummern importiert wird der Compiler anderen Code
//  generiert.

Das gleiche gilt für beliebige Steigerungen der Indirection. Und muss daher mit entsprechender Übersicht benutzt werden.

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

9

12.04.2013, 23:10

Nein, jetzt stellst du den Unterschied wirklich falsch dar. Zwischen text und moretext gibt es keinen grossen Unterschied. Beide sind bei fast allen Anwendungen erstmal einfach die Zahl einer Speicherstelle. Eine Ausnahme ist z.b. sizeof, bei dem einem bekommt man die Groesse einer Zahl, bei der anderen die des Speicherblocks. Zweiter Punkt ist, das es sich bei text um einen konstanten Pointer handelt, du ihn also eigentlich mit einem char * const moretext vergleichen solltest. So ist die Zahl, die bei text gespeichert wird konstant, die bei moretext nicht, da das const dort eben fehlt.

Der entscheidende Unterschied liegt aber in der Beschaffenheit des Speichers, der an der Position liegt, den die Zahlen in text und moretext angeben. Bei der Speicherstelle aus text[] handelt es sich um einen aenderbaren Speicherblock, der in dem Moment angelegt und dann mit dem Inhalt des Literals "Ich bin ein Text" gefuellt wird. Die Speicherstelle von moretext _ist_ die des Literals, es handelt sich um nur lesbarer Speicher, der zur gesamten Programmlaufzeit existiert und diesen Inhalt haelt (daher sollte eigentlich const char *moretext = "Blubb" benutzt werden). Weisst man aber z.b. more die Speicherstelle eines schreibbaren Speicherblocks zu, kann man dort auch schreiben, es handelt sich also gerade nicht um einen Eigenschaft von "moretext" sondern des ihm zugewiesenen Wertes.

Deine Aussagen bzgl. doppelte Indirektion u.ae. sind falsch.

simbad

unregistriert

10

13.04.2013, 08:03



Deine Aussagen bzgl. doppelte Indirektion u.ae. sind falsch.

Das würde mich wundern. Denn genau auf das Ding bin ich auf die Fresse gefallen und habe einen ganzen Tag den Compiler-Output zerlegt, nur um dann vom meinem Bruder, der unter anderem Compiler-Bau studiert hat, erklärt zu kriegen, warum das nicht geht.
Ich habe gerade folgendes gemacht.

a.c

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
#include <stdio.h>

char text[]={"Hallo"};
void output(void);
int main (void) {
    printf("%s\n", text);
    output();
    return (0);
}


b.c

C-/C++-Quelltext

1
2
3
4
5
6
7
#include <stdio.h>

extern char *text;

void output(void) {
    printf("%s\n", text);
}


Und was meinst du wohl was passiert?

Es gibt einen segfault bei der zweiten ausgabe.

Das ist der relevante assembler code in a.c

C-/C++-Quelltext

1
2
        movl    $text, %edi
        call    puts


Das der aus b.c

C-/C++-Quelltext

1
2
3
        movq    text(%rip), %rax
        movq    %rax, %rdi
        call    puts


Und wie man sieht wird eine indirektion mehr generiert.
Nächste mal wenn du jemanden bezichtigst etwas falsches zu erzählen tue dies mit einer entsprechenden Wortwahl. Ich mag es nicht, wenn man mich als Idioten darstellt.

Das ist auch der Grund warum man in C++ bei arrays den delete [] verwenden soll, auch wenn die meisten Compiler das auch ohne [] gebacken kriegen. Wegen groben Unfugs gestrichen.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »simbad« (13.04.2013, 08:39)


Werbeanzeige