Forum: Compiler & IDEs Array von Zeigern und Doppelzeiger


von Smakka (Gast)


Lesenswert?

Wenn ich das hier schreibe:
1
long *zahl[10];

so habe ich dann ein Array von 10 Zeigern geschaffen oder?

Also wenn ich einem von 10 Zeigern eine Adresse geben will, dann muss 
ich schreiben:
1
zahl[5] = &irgendeineZahl

Das habe ich alles ausprobiert. Jedoch gibt es noch die Variante mit dem 
Doppelzeiger, die ich überhaupt nicht verstehe. Daher meine Frage wann 
verwende ich jetzt was?

Gruß Smakka

von Oliver S. (oliverso)


Lesenswert?

Es gibt nicht noch DIE Variante mit Doppelzeigern. Es gibt Zeiger auf 
Zeiger, und die verwendet man halt, wenn man Zeiger auf Zeiger benötigt.

Wenn du da eine konkrete Frage hast, dann zeig das Codebeispiel mit 
Zeiger auf Zeiger, welches du nicht verstehst.

Oliver

von Dr. Sommer (Gast)


Lesenswert?


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Smakka schrieb:
> so habe ich dann ein Array von 10 Zeigern geschaffen oder?

Ja.

Ein solches jedoch „zahl“ zu nennen, ist arg verwirrend …

von Vlad T. (vlad_tepesch)


Lesenswert?

Dr. Sommer schrieb:
> Tipp dazu: http://cdecl.ridiculousfish.com/?q=long+*zahl[10]%3B

nette seite, aber kann mir jemand das Beispiel erklären?

http://cdecl.ridiculousfish.com/?q=%28double+%28%5E%29%28int+%2C+long+long+%29%29foo
1
(double (^)(int , long long ))foo
> cast foo into block (int, long long) returning double

das Dach habe ich noch nie in so einen Kontext gesehen.

von Dr. Sommer (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> das Dach habe ich noch nie in so einen Kontext gesehen.
http://en.wikipedia.org/wiki/Blocks_%28C_language_extension%29
... und keine Sorge, C++11 kann sowas ähnliches auch, und zwar nicht nur 
für Apfeljünger ;)

von Smakka (Gast)


Lesenswert?

Danke schonmal für die Erläuterungen.

Dr. Sommer schrieb:
> Tipp dazu: http://cdecl.ridiculousfish.com/?q=long+*zahl[10]%3B

Das hier ist wirklich sehr interessant

Was genau sind denn jetzt eigentlich die Anwendungen von Zeiger auf 
Zeiger??

Ich habe einmal ein Testprogramm geschrieben, und weiß nicht, ob man es 
auch so richtig macht. Es funktioniert jedenfalls:
1
int main(){
2
    char *zeigerKette[10];
3
    zeigerKette[0] = "Hallo Welt1";
4
    zeigerKette[1] = "Hallo Welt2";
5
    zeigerKette[2] = "Hallo Welt3";
6
7
    printf("%s\n", zeigerKette[0]);
8
    printf("%s\n", zeigerKette[1]);
9
    printf("%s\n", zeigerKette[2]);
10
11
    zeigerKette[0] = "555";
12
13
    printf("%s\n", zeigerKette[0]);
14
    printf("%s\n", zeigerKette[1]);
15
    printf("%s\n", zeigerKette[2]);
16
17
18
    return 0;
19
}

Ich bin mir bei diesen Zeigern nie sicher was man machen darf und was 
nicht. Dann kommen noch diese Zeiger auf Zeiger.

Manchmal sehe ich sowas:
1
long **funtion();

und ich weiß einfach nicht was das ist! Wird hier einfach ein Zeiger auf 
einen Zeiger zurückgegeben? Was hat das für einen Sinn, wenn doch ein 
Zeiger auf Zeiger immer noch ein Zeiger ist. Wieso übergebe ich nicht 
einfach nur einen Zeiger. Das hat doch die selbe Performance oder nicht?

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Die erste Motivation, wenn man Zeiger auf Zeiger benötigt, ist in der 
Argumentliste einer Funktion, die einen übergebenen Zeigen manipulieren 
soll. Und zwar so, dass beim Aufrufer diese Manipulation sichtbar ist.

Mal ein einfacheres Beispiel.
Das hier
1
void foo( int i )
2
{
3
  i = 8;
4
}
5
6
int main()
7
{
8
  int k = 5;
9
10
  foo( k );
11
12
  // hier hat k immer noch den Wert 5
13
}
ist dir hoffentlich klar.
Was passiert hier?
Die Funktion foo bekommt ein Duplikat des Wertes von k. Beim 
Funktionsaufruf wird dieser Wert (5) an i innerhalb der Funktion 
gebunden und steht auch so in der Funktion zur Verfügung. Du kannst i 
innerhalb der Funkion verändern, aber diese Veränderung wirkt sich nicht 
auf k aus, da ja zwischen dem i und dem k keinerlei Verbindung besteht. 
i hat seinen Wert von k erhaltem, aber das wars dann auch schon.

Jetzt gibt es aber Fälle, da möchte man genau das nicht. Man möchte eine 
Funktion in die Lage versetzen, so dass sie auf die Variable des 
Aufrufers 'durchgreifen' kann und die Variable beim Aufrufer verändern 
kann. Wie geht das?
1
void foo( int * i )
2
{
3
  *i = 8;
4
}
5
6
int main()
7
{
8
  int k = 5;
9
10
  foo( &k );
11
12
  // hier hat k den Wert 8 !
13
}

anstett dem Wert von k wird der Funktion die Adresse von k übergeben. 
Diese Adresse wird an i zugewiesen, so dass i damit auf die Variable k 
zeigt. Wird mittels *i der Wert an dieser Adresse verändert, dann 
verändert man damit k, denn die Adresse war ja die Adresse an der k im 
Speicher existiert.

So weit so gut. Das allgemeine Schema ist also.
Sie T ein bestimmter Datentyp, dann ermöglicht man mit dem Schema
1
void foo( T * i )
2
{
3
  *i = Wert der für T zulässig ist;
4
}
5
6
int main()
7
{
8
  T k = Wert;
9
10
  foo( &k );
11
}

der Funktion den Durchgriff auf die Variable des Aufrufers, so dass die 
Funktion die Variable beim Aufrufer ändern kann. Und zwar unabhängig vom 
tatschlichen Datentyp T. In diesem Beispiel war T der Datentyp int.

Was aber, wenn T der Datentyp char * ist?

Folgende Motivation:
Wir haben eine Variable text in main, die auf den konstanten Text 
"Hallo" zeigen soll. Das ist leicht
1
int main()
2
{
3
  char * text = "Hallo";
4
}

Jetzt soll es eine Funktion geben, die text verändern soll. Und zwar 
soll text danach auf den String "Juhu" zeigen. Also im Grunde das hier
1
int main()
2
{
3
  char * text = "Hallo";
4
5
  text = "Juhu";
6
}
würde man danach einen
1
  printf( "%s\n", text );
machen, dann würde das selbstverständlich "Juhu" ausgeben.

Aber: Jetzt wollen wir diese Zuweisung nicht in main machen, sondern in 
einer eigenen Funktion.
Das hier
1
void foo( char * str )
2
{
3
  str = "Juhu";
4
}
5
6
int main()
7
{
8
  char * text = "Hallo";
9
10
  foo( text );
11
}

kann nicht die Lösung sein, denn Analog zum ersten Beispiel mit dem int, 
kriegt die Funktion ja nur eine Kopie der Adresse in text. SIe kann mit 
dieser Adresse machen was sie will, aber sie kann damit nicht text 
selber verändern.
Will eine Funktion eine Variable beim Aufrufer ändern, dann lautete das 
allgemeine Schema
1
void foo( T * i )
2
{
3
  *i = Wert der für T zulässig ist;
4
}
5
6
int main()
7
{
8
  T k = Wert;
9
10
  foo( &k );
11
}
und genau dieses Schema brauchen wir. Bei uns ist T der Datentyp char *, 
und genau das setzen wir da jetzt mal ein anstelle von T ein
1
void foo( char * * str )
2
{
3
  *str = "Juhu";
4
}
5
6
int main()
7
{
8
  char * text = "Hallo";
9
10
  foo( &text );
11
12
  printf( "%s\n", text );
13
}

Jetzt gibt dieses Programm tatsächlich "Juhu" aus. Die Funktion hat über 
einen Pointer auf den Pointer text, die Adresse verändert auf die text 
zeigt.


Willkommen bei deinen ersten Schritten in der Welt der 2-Stern 
Programmierung.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die erste Motivation, wenn man Zeiger auf Zeiger benötigt, ist

... der argv Parameter der main Funktion.

von Smakka (Gast)


Lesenswert?

@Karl Heinz Buchegger: Das war ja wirklich sehr sehr gut erklärt!! Danke 
dafür!

So langsam beginne ich zu verstehen wohin das hinführt. Aber konkret 
meinte ich so ein Beispiel:

Schreiben Sie eine Funktione, die eine Zeichenkette an ein Feld von 
Zeichenketten anhängt und einen Zeiger auf das vergrößerte Feld 
zutrückliefert.
1
char **store_in_array(char **storage, long length, char *newitem);

Ich weiß ich brauche ein Feld von Zeichenketten, aber wie lege ich das 
konkret an? Diese Doppelsterne verwirren mich als, dass sie helfen es 
übersichtlicher zu machen.

von Oliver S. (oliverso)


Lesenswert?

Smakka schrieb:
> Schreiben Sie eine Funktione, die eine Zeichenkette an ein Feld von
> Zeichenketten anhängt und einen Zeiger auf das vergrößerte Feld
> zutrückliefert.

Komisch, wer macht denn in den Ferien Hausaufgabe?

Ein Feld von Zeichenketten:
1
 char *foo[10];

Ein Pointer auf das Feld mit Zeichenketten:
1
 char **pfoo = &(foo[0]);

Das lässt sich auch so schreiben:
1
 char **pfoo = foo;

aber da sieht man das "Pointer auf Pointer" nicht so offensichtlich.

Oliver

von Smakka (Gast)


Lesenswert?

Oliver S. schrieb:
> Ein Feld von Zeichenketten:

Eben da hängt es ja wieder, da ja ein String angehängt werden soll. Das 
heißt ich muss mit Malloc speicher reservieren um ihn eventuell mit 
realloc zu erweitern. Ich weiß aber nicht wie das mit einem Array von 
Zeichenketten  und Malloc gehen könnte. Das ist mir im Moment einwenig 
sehr abstrakt.

Oliver S. schrieb:
> Komisch, wer macht denn in den Ferien Hausaufgabe?

EIgentlich nehme ich über die Ferien Nachhilfe.

Gruß

von Smakka (Gast)


Lesenswert?

SO, habe jetzt folgendes versucht dynamischen Speicher als Array zu 
reservieren, als Vorbereitung für die Strings, jedoch haut das noch 
nicht so ganz hin:
1
int main(){
2
    int **array_2D = calloc(10, sizeof(int*));
3
    int i = 0;
4
    while(i < 10){
5
        *array_2D = calloc(10, sizeof(int));
6
        array_2D++;
7
        i++;
8
    }
9
10
    *(*(array_2D)) = 12;
11
    *(*(array_2D) + 1) = 33;
12
    *(*(array_2D+1)) = 12;
13
    *(*(array_2D+1) + 1) = 33;
14
15
    return 0;
16
}

Ich weiß nicht wieso es nicht funktioniert. Die Schleife passt, jedoch 
stimmt bei der Zeigerarithetik irgendetwas nicht. Ich weiß nur nicht 
was.

Gruß

von Peter II (Gast)


Lesenswert?

Smakka schrieb:
> while(i < 10){
>         *array_2D = calloc(10, sizeof(int));
>         array_2D++;
>         i++;
>     }
>
>     *(*(array_2D)) = 12;

du versauchst dir deine variabel array_2D weil du dir nicht den alten 
wert merkst.
1
for( i = 0; i < 10; ++i ){
2
   array_2D[i] = calloc(10, sizeof(int)); 
3
}

ist doch einfach lesbarer.

von Vlad T. (vlad_tepesch)


Lesenswert?


von Peter II (Gast)


Lesenswert?


von Smakka (Gast)


Lesenswert?

Peter II schrieb:
> for( i = 0; i < 10; ++i ){
>    array_2D[i] = calloc(10, sizeof(int));
> }

Danke, obwohl ich noch nicht ganz verstehe, was dahintersteckt und wieso 
das gleichwertig ist. Werds mir zugemüte ziehen, wenn mein Code 
funktioniert.

Smakka schrieb:
> *(*(array_2D)) = 12;
>     *(*(array_2D) + 1) = 33;
>     *(*(array_2D+1)) = 12;
>     *(*(array_2D+1) + 1) = 33;

Wieso funktioniert das hier nicht?
Ich verstehe das überhaupt nicht.

von Peter II (Gast)


Lesenswert?

Smakka schrieb:
> Danke, obwohl ich noch nicht ganz verstehe, was dahintersteckt und wieso
> das gleichwertig ist. Werds mir zugemüte ziehen, wenn mein Code
> funktioniert.

*(a+i) = a[i]

Smakka schrieb:
>> *(*(array_2D)) = 12;
>>     *(*(array_2D) + 1) = 33;
>>     *(*(array_2D+1)) = 12;
>>     *(*(array_2D+1) + 1) = 33;
>
> Wieso funktioniert das hier nicht?

keine Ahnung was geht dann nicht, bekommst du einen fehler?

lesbar sollte es so genauso gehen

array_2D[0][0] = 12;
array_2D[0][1] = 33;
...

(bin mir nicht 100% sicher, doppelzeiger braucht man recht selten)

von Stefan E. (sternst)


Lesenswert?

Smakka schrieb:
> Smakka schrieb:
>> *(*(array_2D)) = 12;
>>     *(*(array_2D) + 1) = 33;
>>     *(*(array_2D+1)) = 12;
>>     *(*(array_2D+1) + 1) = 33;
>
> Wieso funktioniert das hier nicht?
> Ich verstehe das überhaupt nicht.

Bezogen auf den ursprünglichen Code?
Weil du in der Schleife davor array_2D fleißig inkrementiert hast.

von Smakka (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Weil du in der Schleife davor array_2D fleißig inkrementiert hast.

ohhh ja stimmt. Verdammt!

von Smakka (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Weil du in der Schleife davor array_2D fleißig inkrementiert hast.
1
while(i < 10){
2
        *(array_2D + i) = calloc(10, sizeof(int));
3
        i++;
4
    }

So funktioniert das jetzt!

von Peter II (Gast)


Lesenswert?

Smakka schrieb:
> So funktioniert das jetzt!

und genau das habe ich doch schon oben geschrieben, nur das ich dabei 
noch einen schritt weitergegangen bin.

von Smakka (Gast)


Lesenswert?

Peter II schrieb:
> *(a+i) = a[i]

Dieses Mysterium wird wohl für die nächste Zeit ein Mysterium bleiben. 
Ich muss es wohl akzeptieren.

Ich weiß, dass ein Zeiger:

int *array = malloc(10*sizeof(int));

auch darauf zugegriffen werden kann mit array[i] = WERT;

Jedoch ist mir dabei irgendwie unklar was da C eigentlich macht.

Wenn ich aber schreibe:
1
int **array_2D = calloc(10, sizeof(int*));
2
array_2D[0] = malloc...

greife ich dann mit array_2D[0] auf einen Zeiger zu? Muss ja sein oder? 
Also konkret: Ist array_2D[0] der 0llte Zeiger,sprich der erste Zeiger 
von den 10??

von Peter II (Gast)


Lesenswert?

Smakka schrieb:
> Jedoch ist mir dabei irgendwie unklar was da C eigentlich macht.

genau das gleiche. Es ist am ende nur eine andere Schreibweise,
es ensteht genau der gleiche code.

> greife ich dann mit array_2D[0] auf einen Zeiger zu?
ja, dann es ist nichts anders als ein *(array_2D+0)

von Vlad T. (vlad_tepesch)


Lesenswert?

Smakka schrieb:
> greife ich dann mit array_2D[0] auf einen Zeiger zu? Muss ja sein oder?
> Also konkret: Ist array_2D[0] der 0llte Zeiger,sprich der erste Zeiger
> von den 10??

genau
zum verständis, kannst du den Typ auch durch was anderes substituieren.

1
typedef int* MyType;
2
3
MyType* array_2D = calloc(10, sizeof(MyType)); // legt ein array von 10 Elementen MyType an
4
MyType element0 = array[0];  // ließt das 0. element aus

Das MyType selbst ein Zeiger ist, ist für das Konstrukt irrelevant.
Du solltest in jedem Fall im Hinterkopf haben, dass der Speicher 
uninitialisiert ist, sprich die Zeiger auf zufällige Adressen Zeigen.

Edit:
oh ich seh grad calloc nullt den Speicher sogar

von Smakka (Gast)


Lesenswert?

Ok, lieben lieben Dank an euch. Das ist sehr gut erklärt. Ich wüsste 
nicht was ich machen würde ohne Mikrocontroller.net.


Das mit der Stringtabelle funktioniert trotzdem noch nicht so ganz 
richtig. Wie soll ich denn einen String einlesen und ihn an einem 
dynamischen Speicher anhängen?? Der String steht ja in einem Char array 
und nicht an einer bestimmten Addresse. Da müsste ich für jeden String 
ein eigenes Char Array machen, was aber wiederrum das Konzept der 
dynamischen Speicherung auf den Kopf wirft. Konkret habe ich diesen Code 
hier geschrieben, jedoch keine Idee wie ich das Char Array als eigne 
Adresse in den Speicher verschiebe.
1
int main(){
2
    char string_eingabe[20];
3
4
    char **string_array = calloc(2, sizeof(char*));
5
    string_array[0] = "ErsterString"; /*Genau gleichwertig mit: */ /* *string_array = "Erster String; */
6
    printf("String hinzufuegen>");
7
    scanf("%s", string_eingabe);
8
    
9
    return 0;
10
}

von Smakka (Gast)


Lesenswert?

Also mit Strings, die ich per Code zuweise fuktioniert es einwandfrei.
Hier der Code:
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
char **store_in_array(char **storage, char *newitem);
5
6
int main(){
7
    char **string_array = calloc(0, sizeof(char*));
8
    string_array = store_in_array(string_array, "ersterString");
9
    string_array = store_in_array(string_array, "zweiterString");
10
    string_array = store_in_array(string_array, "dritterString");
11
    string_array = store_in_array(string_array, "vierterString");
12
13
    int i;
14
    for(i = 0; i < 4; i++)
15
        printf("\nString[%d] = %s", i, string_array[i]);
16
17
    return 0;
18
}
19
20
char **store_in_array(char **storage, char *newitem){
21
    static int counter = 0;
22
    counter++;
23
    storage = realloc(storage, counter*sizeof(char*));
24
    storage[counter-1] = newitem;
25
    return storage;
26
}

Was mache ich aber wenn ich einen String einlesen will und dann in das 
dynamische Array hineinladen will.
Dann brauche ich ja einen Puffer in das ich zuerst einlese. Aber was 
kommt danach? Wenn ich im dynamischen Speicher immer nur auf den Puffer 
verweise, so bringt das nichts, denn dieser verändert sich ja immer.
Welche Maßnahmen muss ich hier treffen?

Gruß

von Vlad T. (vlad_tepesch)


Lesenswert?

Smakka schrieb:
> Was mache ich aber wenn ich einen String einlesen will und dann in das
> dynamische Array hineinladen will.

http://stackoverflow.com/questions/4023895/how-to-read-string-entered-by-user-in-c

Smakka schrieb:
> Wenn ich im dynamischen Speicher immer nur auf den Puffer
> verweise, so bringt das nichts, denn dieser verändert sich ja immer.
> Welche Maßnahmen muss ich hier treffen?

du musst für jede Eingabe natürlich einen neuen Puffer holen.

von Smakka (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> du musst für jede Eingabe natürlich einen neuen Puffer holen.

Das meine ich nicht. Ich meine wenn ich schreibe

char *zeichenkette = "Hallo Welt";

dann ist ja der String "Hallo Welt" irgdendwo im Speicher abgelegt, und 
die Addresse wo das ist liegt im Zeiger zeichenkette.

Wenn ich jedoch einen String einlese, so muss ich ihn zuerst in einen 
Puffer einlesen, zum Beispiel so:

char puffer[100];
scanf("%s", puffer);

Und die Frage jetzt natürlich ist, wie ich diesen String einem Feld von 
Zeichenketten hinzufüge, wenn doch das Feld dynamisch sein soll.

Also wie kann ich die zweite Methode wie die erste verwenden? Oder wie 
macht man das generell?

Gruß

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter II schrieb:
> *(a+i) = a[i]

Kann man übrigens auch umdrehen:
1
a + i = &a[i]

D. h. überall, wo man einen Zeiger auf das i-te Element eines Arrays a
braucht, kann man statt der expliziten Adressbildung mit dem &-Operator
auch einfach den Index zum Array (oder Zeiger) addieren.  Die
Zeigersemantik von C bewirkt dabei, dass der Zeiger nicht etwa um i
Bytes, sondern um i Elemente weitergezählt wird.

von Klaus F. (kfalser)


Lesenswert?

Smakka schrieb:
> char puffer[100];
> scanf("%s", puffer);
>
> Und die Frage jetzt natürlich ist, wie ich diesen String einem Feld von
> Zeichenketten hinzufüge, wenn doch das Feld dynamisch sein soll.
>
> Also wie kann ich die zweite Methode wie die erste verwenden? Oder wie
> macht man das generell?

Meinst Du vielleicht

char *zeichenkette = puffer;


Das Anlegen des Speicherplatz braucht's natürlich trotzdem, denn im 1. 
Fall macht das der Compiler, der legt aber nur genau soviel 
Speicherplatz an wie Du für "Hallo Welt" brauchst.

Und bei der 2. Methode musst Du vorher festlegen, wie lang Deine 
maximale Eingabe sein darf (zumindest in C).
Wenn Du
scanf("%s", puffer);
mit einer zu langen Eingabe aufrufst, dann kracht's.

Deshalb ist
scanf("%100s", puffer);
puffer[99] = '\0';

besser

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
Noch kein Account? Hier anmelden.