Forum: Mikrocontroller und Digitale Elektronik Pointer auf Array als Übergabeparameter


von W.B. (Gast)


Lesenswert?

Hallo.

Folgendes Testprogramm in C (MS Compiler und auch GCC) liefert mir immer 
einen ungültigen Zeiger für parray.
Die Zuweisung in get_addr() funktioniert noch, aber sobald die Funktion 
wieder verlassen wird, hat paddr einen ungültigen Wert...
Warum ist das so? Wie löst man das Problem geschickt?
1
char addr[16];
2
3
void init() {
4
    int i = 0;
5
    for (; i < 15; ++i) addr[i] = '0' + i;
6
    addr[16] = 0;
7
}
8
9
void get_addr(char* a) {
10
    a = &addr[0];
11
}
12
13
void main() {
14
    char* paddr = 0;
15
16
    init();
17
18
    get_addr(paddr);
19
20
21
    printf("%s\n", *paddr);
22
}

von W.B. (Gast)


Lesenswert?

parray sollte natürlich paddr heißen.... sorry

von Figlux E. (Firma: figlux electronix) (fuxl)


Lesenswert?

W.B. schrieb:
> get_addr(paddr);

du muss hier die addresse deines zeigers übergeben: get_addr(&paddr);

W.B. schrieb:
> a = &addr[0];

ist bei einem array doppelt gemoppelt -> &addr geht auch

W.B. schrieb:
> char* paddr = 0;

würde ich char *paddr; schreiben

W.B. schrieb:
> void get_addr(char* a)

mach ich auch (char *a) so.

von Cyblord -. (cyblord)


Lesenswert?

Daniel F. schrieb:
> W.B. schrieb:
>> a = &addr[0];
>
> ist bei einem array doppelt gemoppelt -> &addr geht auch

Dann nur addr. Ein Array ist bereits ein Zeiger.

von Lötlackl *. (pappnase) Benutzerseite


Lesenswert?

W.B. schrieb:
> Wie löst man das Problem geschickt?

Gib einen Wert zurück!
1
char* get_addr(char* a) {
2
    a = &addr[0];
3
}
Eigentlich sollte trotzdem eine Warnung hageln.
1
char addr[16];
2
3
void init() {
4
    int i = 0;
5
    for (; i < 15; ++i) addr[i] = '0' + i;
6
    addr[16] = 0; // addr[16] gibt es nicht!!! Geben tut es addr[0] bis addr[15]
7
}
cyblord's Antwort habe ich gerade erst gesichtet, brauche ich ja Daniel 
F. nicht mehr berichtigen.

mfg

von Figlux E. (Firma: figlux electronix) (fuxl)


Lesenswert?

** Lötlackl schrieb:
> char* get_addr(char* a) {
>     a = &addr[0];
> }

kommt auf den compiler an , meines wissen funktioniert es &addr[0], 
&addr und addr.

mfg

von Cyblord -. (cyblord)


Lesenswert?

d_b F. schrieb:
> ** Lötlackl schrieb:
>> char* get_addr(char* a) {
>>     a = &addr[0];
>> }
>
> kommt auf den compiler an , meines wissen funktioniert es &addr[0],
> &addr und addr.
>
> mfg

&addr funktioniert natürlich, aber es gibt dir die Adresse des Pointers 
und nicht die Adresse des ersten Arrayelements. Weil, wie gesagt, ein 
Array bereits ein Pointer ist.

gruß cyblord

von Mikel M. (mikelm)


Lesenswert?

W.B. schrieb:
> Hallo.
>
> Folgendes Testprogramm in C (MS Compiler und auch GCC) liefert mir immer
> einen ungültigen Zeiger für parray.
> Die Zuweisung in get_addr() funktioniert noch, aber sobald die Funktion
> wieder verlassen wird, hat paddr einen ungültigen Wert...
> Warum ist das so? Wie löst man das Problem geschickt?
>
>
1
char addr[16];
2
> 
3
> void init() {
4
>     int i = 0;
5
>     for (; i < 15; ++i) addr[i] = '0' + i;
6
>     addr[16] = 0;
7
hier muß 15 hin
8
 entspricht char addr[] ="0123456789:;<=>";
9
 vermutlich moechtest Du 
10
     char addr[] ="0123456789abcdef";
11
> }
12
> 
13
> void get_addr(char* a) {
14
>     a = &addr[0];
15
> }
16
 Als define würde es gehen.
17
 #define get_addr(a)  (a) = &addr[0];
18
 so weist du a zwar nen Wert zu, aber am ende der function geht er verloren.
19
 könnte der compiler also wegoptimieren.
20
21
falsch waere 
22
   *a = addr[0];  // !! Speicherplatz undefiniert, Nullpointer zugriff geht nur wenn der übergebene string auch nen speicherplatz hat
23
24
> 
25
> void main() {
26
>     char* paddr = 0;
27
  Da du nen Zeiger anlegst schreibt man char* paddr = NULL;
28
> 
29
>     init();
30
> 
31
>     get_addr(paddr);
32
> 
33
> 
34
>     printf("%s\n", *paddr);
35
 hier willst du einen string ausgeben %s , übergibst aber einen char.
36
 also entweder
37
     printf("%c\n", *paddr); // Das Zeichen ausgeben
38
oder 
39
     printf("%s\n", paddr);  // wobei hier jetzt der reststring ausgegeben würde
40
41
42
> }

von Lötlackl *. (pappnase) Benutzerseite


Lesenswert?

Besser scheint, weil sonst schon wieder eine Warnung droht,
1
char* get_addr(char* a) {
2
    a = &addr[0];
3
    return a;
4
}
.
Das ist aber nicht immer so (mache gerade ein paar ARM-Geschichten, da 
gibt es keine Warnung), wird wohl vermutlich an irgendwelchen 
Übersetzungsoptionen liegen.

von Karl H. (kbuchegg)


Lesenswert?

cyblord ---- schrieb:
> d_b F. schrieb:
>> ** Lötlackl schrieb:
>>> char* get_addr(char* a) {
>>>     a = &addr[0];
>>> }
>>
>> kommt auf den compiler an , meines wissen funktioniert es &addr[0],
>> &addr und addr.
>>
>> mfg
>
> &addr funktioniert natürlich, aber es gibt dir die Adresse des Pointers
> und nicht die Adresse des ersten Arrayelements. Weil, wie gesagt, ein
> Array bereits ein Pointer ist.

Vorsicht:
Ein Array ist kein Pointer!

&addr  ist eine sinnlose Sache. Denn addr alleine degeneriert bereits 
automatisch zur Adresse des ersten Array Elements. Damit existiert &addr 
eigentlich nicht. Denn die Adresse des ersten Arrayelements hat 
seinerseits keine Adresse. Sie ist kein Objekt im Speicher, sondern eine 
Zahlenkonstante. Ist im Grunde gleich zu: &5
Und das ist keine sinnvolle Operation.
Viele Compiler akzeptieren das aber trotzdem und werten es gleich wie 
addr.

1
char* getAddr( void )
2
{
3
  return addr;
4
}
ist die perfekte Schreibweise dafür.

von Karl H. (kbuchegg)


Lesenswert?

Will man unbedingt die ganze Sache über die Argumentliste abwickeln, 
dann geht die Sache so.


Nehmen wir mal einen einfacheren und weniger verwirrenden Fall her. Ein 
int soll über die Argumentliste verändert werden.

So gehts nicht
1
void foo( int i )
2
{
3
  i = 5;
4
}
5
6
int main()
7
{
8
  int k = 8;
9
  foo( k );
10
  // k ist immer noch 8
11
}

warum gehts so nicht?
Weil die Funktion nur eine Kopie des Wertes in k bekommt. Wird die 
Funktion aufgerufen, so wird die neue Variable i innerhalb der Funktion 
erzeugt und sie bekommt eine Kopie des Wertes von k. Diese Kopie kann 
die Funktion aber ändern wie sie lustig ist. Davon ändert sich k nicht.

Will man das erreichen, dann muss man die Adresse von k an foo 
übergeben, damit foo über diese Adresse an k herankommt
1
void foo( int* i )
2
{
3
  *i = 5;
4
}
5
6
int main()
7
{
8
  int k = 8;
9
  foo( &k );
10
  // jetzt hat k den Wert 5
11
}

Der entscheidende Unterschied ist hier, dass in main
1
  ...
2
  foo( &k );
3
  ...
die Adresse der Variablen übergeben wird! Nur so, wenn die Funktion die 
Speicheradresse von k hat, kann die Funktion auf die Variable k 
zugreifen.

Die Funktion muss dafür als Argument einen Pointer haben
1
void foo( int* i )
und muss über eine Derefernzierung
1
  *i = 5;
den Wert zuweisen.

Das allgemeine Schema lautet daher:
Gegeben sei ein Datentyp T.
Damit eine Funktion einen Wert vom Typ T beim Aufrufer verändern kann, 
muss der Aufbau so aussehen
1
void foo( T* ptr )
2
{
3
  *ptr = irgend_ein_Wert_der_zu_T_passt;
4
}
5
6
int main()
7
{
8
  T variable;
9
10
  foo( &variable );
11
}

Das ist das allgemeine Schema, nach dem das läuft.

Nun. Im Falle des Fragestellers: Was ist der Datentyp T?
T ist in seinem Fall char*

Also setzen wir in das allgemeine Schema für T einfach mal char* ein
(einfach wie bei Copy&Paste alle T durch char* ersetzen):
1
void foo( char** ptr )
2
{
3
  *ptr = irgend_ein_Wert_der_zu_T_passt;
4
}
5
6
int main()
7
{
8
  char* xyz;
9
  foo( &xyz );
10
}

jetzt braucht man noch etwas vernünftiges für 
"irgend_ein_Wert_der_zu_T_passt". Na, zb die Adresse des ersten Elements 
des Arrays, vulgo "die Adresse des Arrays". Die einfache Nennung des 
Arraynamens reicht dafür schon. Denn: Wird ein Array in dieser Form 
benutzt (also ohne Indexoperation), dann steht der Name des Arrays für 
seine Startadresse (vulgo: es degeneriert zu einem Pointer. Aber 
Achtung: Ein Array IST deswegen noch lange kein Pointer. Ein Pointer ist 
selbst eine Variable, die irgendwo im Speicher haust und eine Adresse 
hat.)
1
char test[5];
2
3
void foo( char** ptr )
4
{
5
  *ptr = test;
6
}
7
8
int main()
9
{
10
  char* xyz;
11
  foo( &xyz );
12
}



Willkommen in der 2-Stern Progammierung!
Lass dich nicht von char** verwirren. Die 2 Sterne entstehen dadurch, 
dass du T durch char* ersetzen musst. Und daher wird dann T* zu char**




Die restlichen Probleme im Code wurden ja schon angesprochen.

von W.B. (Gast)


Lesenswert?

Danke, jetzt hab ich's gecheckt.
So funktionierts....


[c]char addr[16];

void init() {
    int i = 0;
    for (; i < 15; ++i) addr[i] = '0' + i;
    addr[15] = 0;
}

void get_addr(char** a) {
    *a = addr;
}

void main() {
    char* paddr = 0;

    init();

    get_addr(&paddr);


    printf("%s\n", paddr);
}[\c]

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.