Forum: Compiler & IDEs Pointer, die 17324ste


von Pointer-Missversteher (Gast)


Lesenswert?

1
    char *p_string = "ABCDE";  //printf ("%s\r\n",p_string); ergibt ABCDE
2
    *p_string++;               //printf ("%s\r\n",p_string); ergibt BCDE
3
    p_string++;                //printf ("%s\r\n",p_string); ergibt CDE

Wo ist der funktionale Unterschied zwischen den beiden letzten Zeilen, 
wenn doch eigentlich das gleiche bewirkt wird?

von BT (Gast)


Lesenswert?

Hallo,


Du solltest eben das ganze dann nochmal neu initialiseren bzw. den 
Pointer dann auch wieder auf den Anfang bzw. auf "START" setzen.

Gruß
BT

von Oliver (Gast)


Lesenswert?

Was sagt denn dein C-Buch zu dem Thema?

Was macht der Operator ++ ?
Was macht der Operator * ?

Welche Warnungen zeigt dir der Compiler? Und warum?

Und, last but not least, was findest du per google zu dem Thema?

Fragen über Fragen...

Oliver

von Matthias L. (Gast)


Lesenswert?

Ganz einfach:
1
*p_string++;

ist die Kurzform für:
1
*p_string;
2
 p_string = p_string + sizeof(*p_string);

und von
1
 p_string = p_string + sizeof(*p_string);

Das *p_string mit oder ohne ++ dahinter besagt, das du auf den Pointer 
zugreifen möchstest und das willst, wo der Pointer hinzeigt.

Anhand printf siehst du das auch. jedesmal wenn du ++ ausführst, zeigt 
printf ein Zeichen später an.

das
*p_string(++) macht erst mit einer expliziten Zuweisung Sinn. zB sowas:

UDR = *p_string++;

Hier wird erst auf den Pointer zugegriffen (*p_string) und danach dann 
der Pointer selbst um ein Element erhöht (++)..

beliebt ist das beim Ausgeben von Zeichenketten zB über die UART:
1
void Senden ( uint8_t* p_string )
2
{
3
  while ( *p_string )
4
  {
5
    UDR = *p_string++;
6
    // warte hier bis UDR gesendet wurde
7
  }
8
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Matthias Lipinsky schrieb:
> p_string = p_string + sizeof(*p_string);

Vorsicht. Das ist nur zulässig, wenn sizeof hier 1 liefert.

Wäre das beispielsweise ein int-Array, würde das nicht das gleiche 
machen:
1
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
2
3
int* p = array;
4
5
printf("Wert %d\n", *p);
6
7
// ergibt 0
8
9
p++;
10
11
printf("Wert %d\n", *p);
12
13
// ergibt 1
14
15
p = p + 1;
16
17
printf("Wert %d\n", *p);
18
19
// ergibt 2
20
21
p = p + sizeof (*p);
22
23
printf("Wert %d\n", *p);
24
25
// ergibt 4 oder 6!

Die unterschiedlichen Möglichkeiten im letzten Schritt sind auf 
unterschiedliche Größen von int zurückzuführen - auf einem 
16-Bit-System ist sizeof (int) = 2, auf einem 32-Bit-System ist sizeof 
(int) = 4.

von Pointer-Missversteher (Gast)


Lesenswert?

Im Ritchie-Buch hab ich das hier gefunden:
1
/* strcmp:  return <0 if s<t, 0 if s==t, >0 if s>t */
2
3
   int strcmp(char *s, char *t)
4
   {
5
       int i;
6
       for (i = 0; s[i] == t[i]; i++)
7
           if (s[i] == '\0')
8
               return 0;
9
       return s[i] - t[i];
10
   }

Wenn ich das richtig sehe entspricht *s somit s[0].

Wenn ich nun schreibe:
1
char *p_string = "ABCDE";  //printf ("%s\r\n",p_string); ergibt ABCDE
2
p_string[0] = 'X';
würde ich eigentlich XBCDE in der Ausgabe erwarten. Stattdessen wird wie 
im vorherigen Fall ABCDE ausgegeben.
Warum?

von Rene H. (Gast)


Lesenswert?

1
int main (int argc, char *argv[])
2
{
3
  char ptr[] = "ABCDE";
4
5
  fprintf (stdout, "Ptr: %s\n", ptr);
6
7
  ptr[0] = 'X';
8
9
  fprintf (stdout, "Ptr: %s\n", ptr);
10
11
  return (0);
12
}

Ausgabe:

./tmp2
Ptr: ABCDE
Ptr: XBCDE


Grüsse,
René

von Klaus W. (mfgkw)


Lesenswert?

Pointer-Missversteher schrieb:
> würde ich eigentlich XBCDE in der Ausgabe erwarten. Stattdessen wird wie
> im vorherigen Fall ABCDE ausgegeben.

Das ist nicht zulässig und das Ergebnis abhängig von deinem System.
Grund: "ABCDE" ist eine Stringkonstante, die du gar nicht ändern darfst.

von Pointer-Missversteher (Gast)


Lesenswert?

Klar, mit nem Array geht das natürlich.
Aber wieso geht es nicht mit einem String?

von Pointer-Missversteher (Gast)


Lesenswert?

>Das ist nicht zulässig und das Ergebnis abhängig von deinem System.
>Grund: "ABCDE" ist eine Stringkonstante, die du gar nicht ändern darfst.

Stringkonstante. Das erklärt einiges.

von Rene H. (Gast)


Lesenswert?

Pointer-Missversteher schrieb:
> , mit nem Array geht das natürlich.
> Aber wieso geht es nicht mit einem String?

Weil wie erwähnt, Konstante. Würdest Du das auf einem Linux System 
machen bekommst Du direkt ein Core File.

Grüsse,
René

von Matthias L. (Gast)


Lesenswert?

>Matthias Lipinsky schrieb:
>> p_string = p_string + sizeof(*p_string);

>Vorsicht. Das ist nur zulässig, wenn sizeof hier 1 liefert.


Du hast recht. Aber es sollte ja eins liefern, da der Zeiger als Zeiger 
auf char definiert ist. Aber das sollte doch auch funktionieren:
1
uint32_t   au32Value[..] = {...};
2
uint32_t  *pu32Ptr;
3
-----
4
pu32Ptr = &au32Value;
5
for (...)
6
{
7
  ... = *pu32Ptr++
8
}


Das x = x + sizeof(*x) hab ich von "meinem" SPS Programmierungen. Da ist 
das der Weg, den Zeiger auf das nächste Element zu schieben. Dort 
liefert sizeof(pointer-dereferenziert) immer die Länge des Elementes in 
Bytes. aber ein pointer++ berücksichtigt das ja vom Compiler her 
automatisch.

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:


> Bytes. aber ein pointer++ berücksichtigt das ja vom Compiler her
> automatisch.


Genau.
Und wenn man daher die Operation durch andere C-Operationen nachbilden 
will, muss man das ebenfalls berücksichtigen.
1
  p_string = ((char*)p_string) + sizeof(*p_string);

der Cast ist genau aus diesem Grunde notwendig, damit nicht der Compiler 
seinerseits bei der Addition nochmal die sizeof mit einrechnet. Denn 
sizeof char ist per Definition 1.

Jetzt stimmt es immer.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

um nochmal auf seine ursprüngliche frage einzugehen:

Die Antwort die er vermutlich hören sollte, ist: ++ bindet stärker als *

*string++ ist also dasselbe wie *(string++)
und nicht wie man vielleicht annehmen könnte, (*string)++

Der Compiler sollte eine Warnung "value computed is not used" werfen, 
weil das Ergebnis zwar berechnet wird, (nämlich der Inhalt von *string), 
danach wird der Pointer erhöht, das Ergebnis selbst aber nie verwendet.

von Pointer-Missversteher (Gast)


Lesenswert?

>++ bindet stärker als *
Ich sag mal so: in diesem 'speziellen' oberen Fall 'bindet' es überhaupt 
nicht... denn ich hab mich gefragt wie ich das Element um ein erhöhen 
(verändern) könnte - was wie sich nun herausgestellt hat, innerhalb 
eines Strings gar nicht geht.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Pointer-Missversteher schrieb:
>>++ bindet stärker als *
> Ich sag mal so: in diesem 'speziellen' oberen Fall 'bindet' es überhaupt
> nicht... denn ich hab mich gefragt wie ich das Element um ein erhöhen
> (verändern) könnte - was wie sich nun herausgestellt hat, innerhalb
> eines Strings gar nicht geht.

Es bindet schon, und wenn der String keine Konstante wäre, täte es (mit 
der "anderen" Klammerung (*string)++ ) auch den Inhalt des Strings 
ändern.

von Karl H. (kbuchegg)


Lesenswert?

Pointer-Missversteher schrieb:
>>++ bindet stärker als *
> Ich sag mal so: in diesem 'speziellen' oberen Fall 'bindet' es überhaupt
> nicht...

Doch das tut es.

> denn ich hab mich gefragt wie ich das Element um ein erhöhen
> (verändern) könnte - was wie sich nun herausgestellt hat, innerhalb
> eines Strings gar nicht geht.

'nicht geht' ist ein gar grauslich Wort.
Die C-Sprachdefinition lässt die Frage ob "geht oder nicht geht" offen, 
indem sie die Operation in diesem speziellen Fall (versuchte Veränderung 
eines String-Literals) zu einer undefinierten Operation erklärt. Alles 
ist möglich - inklusive einem Verhalten, das dem Erwarteten entspricht 
(was immer du auch erwartet hast). Das man auf undefiniertem Verhalten 
natürlich keine zuverlässigen Programme aufbauen kann, sollte auch klar 
sein.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Matthias Lipinsky schrieb:
> aber ein pointer++ berücksichtigt das ja vom Compiler her
> automatisch.

Das geschieht bei jeder Pointerarithmetik.

Also auch bei

  pointer += Wert

und natürlich auch bei

  pointer = pointer + Wert

> Das x = x + sizeof(*x) hab ich von "meinem" SPS Programmierungen. Da ist
> das der Weg, den Zeiger auf das nächste Element zu schieben.

Wenn das C oder C++ sein soll, ist das eine Fehlerquelle. Denn es ist 
nur für den Sonderfall, daß sizeof 1 liefert, korrekt.

Sieh Dir mein Beispiel nochmal genau an.

von Matthias L. (Gast)


Lesenswert?

Dazu folgende Frage:
16Bit => 2 und 32bit => 4.
logisch.
Aber sollte dann nicht auch das Array
int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
so im Speicher liegen:
1
Byteadresse     0   1   2   3   4   5   6   7   8   9  ...
2
16bit           0   0   1   0   2   0   3   0   4   0
3
32bit           0   0   0   0   1   0   0   0   2   0
4
??
Ansonsten bedeutet das ja, das im C das p von hier:
int* p = array;
ja eigentlich keine "Byte"adresse sein kann??

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
> Dazu folgende Frage:
> 16Bit => 2 und 32bit => 4.
> logisch.
> Aber sollte dann nicht auch das Array
> int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
> so im Speicher liegen:
>
1
> Byteadresse     0   1   2   3   4   5   6   7   8   9  ...
2
> 16bit           0   0   1   0   2   0   3   0   4   0
3
> 32bit           0   0   0   0   1   0   0   0   2   0
4
> ??
5
>

Genau das tut es auch. (Ich lass jetzt mal die ganze Problematik über 
Endianess weg, denn deine Frage zielt ja ganz klar nicht darauf ab). 
Also: die einzelnen Elemente liegen bytemässig direkt hintereinander, 
wobei jedes Element genau so viele Bytes braucht wie eben der 
sizeof(*array) aussagt.

> Ansonsten bedeutet das ja, das im C das p von hier:
> int* p = array;
> ja eigentlich keine "Byte"adresse sein kann??

nochmal.
Wenn du Pointer Arithmetik machst (egal welche), dann wird vom Compiler 
IMMER die sizeof des zugrunde liegenden Datentyps mit eingerechnet. Das 
ist so definiert und es gibt nichts was man dagegen tun kann (ausser mit 
einem cast eingreifen und sein eigenes Süppchen kochen).
1
  ptr++   <==>  ptr += 1   <==>   ptr = ptr + 1

das das worauf der ptr zeigt unterschiedliche sizeof haben kann, braucht 
dich als C-Programmierer nicht kümmern, das erledigt der Compiler wenn 
der die Addition umsetzt. Deswegen definierst du ja auch nicht einfach 
nur einen Pointer, sondern du definierst eine Pointervariable als 
'Pointer auf int' oder 'Pointer auf double' oder ....

wenn du schreibst
1
  int * ptr = (int*)0x100;
2
3
  ptr++;

und auf deinem System ist sizeof(int) gleich 2, dann ist der numerische 
Wert des Pointers nach der ++ Operation nicht 0x101, sondern 0x102.
Wäre die sizeof(int) gleich 4, dann wäre der numerische Wert des 
Pointers 0x104, obwohl du in beiden Fällen lediglich einen immer 
gleichen ++ angewendet hast.

von Matthias L. (Gast)


Lesenswert?

das ist der entscheidene Satz:
>dann wird vom Compiler IMMER die sizeof des zugrunde liegenden Datentyps >mit 
eingerechnet.

Die SPS Compiler machen das eben nicht. Kurzschreibweisen a la p++ gibt 
es hier nicht. das heisst immer p:= p + offset. Und Offset bestimmt man 
hier zwangsläufig mit sizeof(). Der liefert immer die Länge des 
Datentypes, wo der Pointer hinzeigt. Ok. Kapiert. Ich bleib einfach bei 
p++ ;-)

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
> das ist der entscheidene Satz:
>>dann wird vom Compiler IMMER die sizeof des zugrunde liegenden Datentyps >mit
> eingerechnet.

Eine andere Sache, an der sich dieses Konzept durchschlägt, ist der sog. 
Pointer-Array Dualismus.

Die Array-Index Operation ist in C als Variante der Pointer Arithmetik 
definiert.
1
    array[index]   <==>  *(array + index)


genau deswegen kann man das hier machen
1
  int a[3] = { 0, 1, 2 };
2
3
  int j = *(a + 2);
4
  int k = a[2];
5
6
  // -> j == k
aber aus demselben Grund kann man auch das hier machen
1
  int * ptr = malloc( 5 * sizoef(int) );
2
3
  ptr[3] = 8;

Also einen Pointer wie ein Array auffassen und mit Indexoperationen 
zugreifen.

Und nicht zuletzt spielt das ganze dann ja auch in die berühmte C 
'Anomalie' mit rein, nach der Arrays an Funktionen übergeben werden, 
indem man die Startadresse des Arrays übergibt
1
void foo( int * a )
2
{
3
  a[4] = 3;
4
}
5
6
int main()
7
{
8
  int b[5];
9
  foo( b );
10
}
Die Funktion bekommt einen Pointer. Was mich aber nicht daran hindert, 
Indexoperationen damit zu betreiben.

bei all diesen Dingen ist der Array-Pointer Dualismus im Spiel und die 
simple Definition, wonach Array Indizierung nichts anders als eine 
andere Schreibweise für eine bestimmte Pointer-Arithmetik ist und der 
Compiler bei Pointer-Arithmetik immer die sizeof des Datentyps mit 
einrechnen muss. Sonst würde obiges nämlich alles nicht funktionieren. 
Zumindest nicht so.

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


Lesenswert?

Matthias Lipinsky schrieb:
> Die SPS Compiler machen das eben nicht.

Es ist natürlich ausgesprochen dämlich, wenn jemand für eine andere
Programmiersprache offenbar zumindest Teile der C-Syntax benutzt,
aber nicht die gleiche Semantik wie in C dann damit implementiert.

von Matthias L. (Gast)


Lesenswert?

Das leuchtet alles ein. Nur das hier:
1
void foo( int * a )
2
{
3
  a[4] = 3;
4
}
interpretiere ich so: Die Funktion bekommt einen Zeiger auf EINEN int. 
Das da jetzt im Speicher weitere ints dahinter stehen, ist nur durch die 
Deklaration als Array gegeben, aber der Funktion nicht bekannt. Das 
sollte doch stimmen, denn die Funktions selbst weiss ja nicht, wie lang 
das array ist..? Denn das:
  a[4] = 3;
wird ja lt deiner Erklärung zu *(a+4) = 3. Egal ob diese Speicherzelle 
noch zum Array gehört oder nicht.

Richtig?


>Es ist natürlich ausgesprochen dämlich, wenn jemand für eine andere
>Programmiersprache offenbar zumindest Teile der C-Syntax benutzt,
>aber nicht die gleiche Semantik wie in C dann damit implementiert.

Richtig. Es war unklug von mir, das nicht eindeutig zu kennzeichnen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Matthias Lipinsky schrieb:
> interpretiere ich so: Die Funktion bekommt einen Zeiger auf EINEN int.

Nein. Sie bekommt einen Zeiger auf int. Ob das einer ist, oder ob das 
ein Array ist (also im Speicher mehrere hintereinander angeordnet sind), 
ist für die Funktion (und auch in der Handhabung) nicht unterscheidbar.

Der Aufrufer einer solchen Funktion muss genau wissen, was er tut, oder 
das Programmdesign muss dahingehend angepasst werden, daß der Funktion 
zusätzlich zum Pointer auch noch eine Größeninformation (Anzahl der 
Elemente) mitgeliefert wird.

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


Lesenswert?

Matthias Lipinsky schrieb:
> Das sollte doch stimmen, denn die Funktions selbst weiss ja nicht, wie
> lang das array ist..?

Ja, so ist es.  Daher kann die aufgerufene Funktion in C ja auch
keinen array bounds check machen.

von Matthias L. (Gast)


Lesenswert?

Ein Moderator sagt nein, der andere ja. Aber ich denke, ich weiss was 
ihr meint und ich habe soweit recht und das verstanden. Sinnvoll wäre 
sozusagen das:
1
void foo( int *a, int len_of_a )
2
{
3
  ...
4
}

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:
> Das leuchtet alles ein. Nur das hier:
>
1
> void foo( int * a )
2
> {
3
>   a[4] = 3;
4
> }
5
>
> interpretiere ich so: Die Funktion bekommt einen Zeiger auf EINEN int.

Du musst das EINEN nicht groß schreiben.

> Richtig?

Alles richtig.

Einigen wir uns auf: Die Funktion bekommt einen Zeiger und weiß nicht 
auf wieviele hintereinanderliegende int dieser Zeiger zeigt.
Dieses Wissen steckt beim Programmierer und der muss dafür sorgen, dass 
alles mit rechten Dingen vor sich geht. Nur mit dem Zeiger alleine ist 
es der Funktion unmöglich, das auf eigene Faust herauszufinden.


Das wird übrigens auch nicht dadurch besser, dass es eine alternative 
Schreibweise gibt
1
void foo( int a[] )
2
{
3
  ...

hier sieht der Programmierer besser, dass die Funktion ein Array 
erwartet. Aber das ist lediglich 'syntactic sugar'. Es unterscheidet 
sich in nichts von der Pointer Schreibweise.

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


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das wird übrigens auch nicht dadurch besser, dass es eine alternative
> Schreibweise gibt
>
1
void foo( int a[] ) 
2
> { 
3
>   ...
>
> hier sieht der Programmierer besser, dass die Funktion ein Array
> erwartet.

Oder noch schlimmer:
1
void foo(int a[25]) { ...

Die 25 ist weiter nichts als ein Kommentar.  Der kann stimmen, muss
es aber nicht.

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.