Hier ist eine detaillierte Erklärung, von der ich hoffe, dass sie hilfreich ist. Beginnen wir mit Ihrem Programm, da es am einfachsten zu erklären ist.
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
Die erste Aussage:
const char* p = "Hello";
deklariert p
als Zeiger auf char
. char
Was bedeutet das, wenn wir "Zeiger auf a " sagen ? Dies bedeutet, dass der Wert von p
die Adresse von a ist char
; p
sagt uns, wo im Speicher etwas Platz für a ist char
.
Die Anweisung wird auch initialisiert p
, um auf das erste Zeichen im Zeichenfolgenliteral zu verweisen "Hello"
. Für diese Übung ist es wichtig zu verstehen p
, dass nicht auf die gesamte Zeichenfolge, sondern nur auf das erste Zeichen verwiesen wird 'H'
. Immerhin p
ist ein Zeiger auf eins char
, nicht auf die gesamte Zeichenfolge. Der Wert p
ist die Adresse des 'H'
in "Hello"
.
Dann richten Sie eine Schleife ein:
while (*p++)
Was bedeutet die Schleifenbedingung *p++
? Drei Dinge wirken hier rätselhaft (zumindest bis Vertrautheit einsetzt):
- Der Vorrang der beiden Operatoren Postfix
++
und Indirektion*
- Der Wert eines Postfix-Inkrementausdrucks
- Der Nebeneffekt eines Postfix-Inkrementausdrucks
1. Vorrang . Ein kurzer Blick auf die Prioritätstabelle für Operatoren zeigt, dass das Postfix-Inkrement eine höhere Priorität hat (16) als die Dereferenzierung / Indirektion (15). Dies bedeutet, dass der komplexe Ausdruck wie *p++
folgt gruppiert wird : *(p++)
. Das heißt, das *
Teil wird auf den Wert des p++
Teils angewendet . Nehmen wir also p++
zuerst den Teil.
2. Postfix-Ausdruckswert . Der Wert von p++
ist der Wert p
vor dem Inkrement . Wenn Sie haben:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Die Ausgabe lautet:
7
8
weil i++
bis i
vor dem Inkrement ausgewertet . Ebenso p++
wird auf den aktuellen Wert von ausgewertet p
. Wie wir wissen, ist der aktuelle Wert von p
die Adresse von 'H'
.
Nun wurde der p++
Teil von *p++
bewertet; Es ist der aktuelle Wert von p
. Dann *
passiert der Teil. *(current value of p)
bedeutet: Zugriff auf den Wert unter der Adresse von p
. Wir wissen, dass der Wert an dieser Adresse ist 'H'
. Der Ausdruck *p++
ergibt also 'H'
.
Moment mal, sagst du. Wenn dies *p++
ausgewertet wird 'H'
, warum wird das nicht 'H'
im obigen Code gedruckt? Hier kommen Nebenwirkungen ins Spiel.
3. Nebenwirkungen des Postfix-Ausdrucks . Das Postfix ++
hat den Wert des aktuellen Operanden, hat jedoch den Nebeneffekt, dass dieser Operand inkrementiert wird. Huh? Schauen Sie sich diesen int
Code noch einmal an:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Wie bereits erwähnt, lautet die Ausgabe wie folgt:
7
8
Wenn i++
es im ersten printf()
bewertet wird, wird es mit 7 bewertet. Der C-Standard garantiert jedoch, dass irgendwann vor printf()
Beginn der Ausführung des zweiten die Nebenwirkung des ++
Bedieners aufgetreten ist. Das heißt, bevor das zweite printf()
passiert, wurde i
es aufgrund des ++
Operators im ersten erhöht printf()
. Dies ist übrigens eine der wenigen Garantien, die der Standard für das Timing von Nebenwirkungen gibt.
Wenn der Ausdruck in Ihrem Code *p++
ausgewertet wird, wird er ausgewertet 'H'
. Aber wenn Sie dazu kommen:
printf ("%c", *p)
diese lästige Nebenwirkung ist aufgetreten. p
wurde erhöht. Whoa! Es zeigt nicht mehr auf 'H'
, sondern auf eine vergangene Figur 'H'
: mit 'e'
anderen Worten auf die. Das erklärt Ihre übermütige Leistung:
ello
Daher der Chor hilfreicher (und genauer) Vorschläge in den anderen Antworten: Um die empfangene Aussprache "Hello"
und nicht ihr Cockney-Gegenstück zu drucken , benötigen Sie so etwas wie
while (*p)
printf ("%c", *p++);
So viel dazu. Was ist mit dem Rest? Sie fragen nach den Bedeutungen dieser:
*ptr++
*++ptr
++*ptr
Wir haben gerade über das erste gesprochen, also schauen wir uns das zweite an : *++ptr
.
Wir haben in unserer früheren Erklärung gesehen, dass das Postfix-Inkrement p++
eine bestimmte Priorität , einen Wert und einen Nebeneffekt hat . Das Präfixinkrement ++p
hat den gleichen Nebeneffekt wie sein Postfix-Gegenstück: Es erhöht seinen Operanden um 1. Es hat jedoch eine andere Priorität und einen anderen Wert .
Das Präfixinkrement hat eine niedrigere Priorität als das Postfix. es hat Vorrang 15. Mit anderen Worten, es hat den gleichen Vorrang wie der Dereferenzierungs- / Indirektionsoperator *
. In einem Ausdruck wie
*++ptr
Was zählt, ist nicht Vorrang: Die beiden Operatoren haben Vorrang. Die Assoziativität setzt also ein. Das Präfixinkrement und der Indirektionsoperator haben eine Assoziativität von rechts nach links. Aufgrund dieser Assoziativität wird der Operand ptr
mit dem Operator ganz rechts ++
vor dem Operator weiter links gruppiert *
. Mit anderen Worten, der Ausdruck wird gruppiert *(++ptr)
. Wie bei, *ptr++
aber aus einem anderen Grund, wird das *
Teil auch hier auf den Wert des ++ptr
Teils angewendet .
Was ist dieser Wert? Der Wert des Präfix-Inkrement-Ausdrucks ist der Wert des Operanden nach dem Inkrement . Dies macht es zu einem ganz anderen Tier als der Postfix-Inkrement-Operator. Angenommen, Sie haben:
int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
Die Ausgabe wird sein:
8
8
... anders als beim Postfix-Operator. Ebenso, wenn Sie haben:
const char* p = "Hello";
printf ("%c ", *p); // note space in format string
printf ("%c ", *++p); // value of ++p is p after the increment
printf ("%c ", *p++); // value of p++ is p before the increment
printf ("%c ", *p); // value of p has been incremented as a side effect of p++
Die Ausgabe lautet:
H e e l // good dog
Verstehst du warum?
Nun kommen wir zu dem dritten Ausdruck, nach dem Sie gefragt haben ++*ptr
. Das ist eigentlich das Schwierigste. Beide Operatoren haben die gleiche Priorität und Rechts-Links-Assoziativität. Dies bedeutet, dass der Ausdruck gruppiert wird ++(*ptr)
. Das ++
Teil wird auf den Wert des *ptr
Teils angewendet .
Also wenn wir haben:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
Die überraschend egoistische Ausgabe wird sein:
I
Was?! Okay, das *p
Teil wird also ausgewertet 'H'
. Dann ++
kommt das ins Spiel, an welchem Punkt es 'H'
auf den Zeiger angewendet wird , überhaupt nicht auf den Zeiger! Was passiert, wenn Sie 1 hinzufügen 'H'
? Sie erhalten 1 plus den ASCII-Wert von 'H'
72; Sie erhalten 73. Stellen Sie das als dar char
, und Sie erhalten das char
mit dem ASCII-Wert von 73 : 'I'
.
Das kümmert sich um die drei Ausdrücke, nach denen Sie in Ihrer Frage gefragt haben. Hier ist eine andere, die im ersten Kommentar zu Ihrer Frage erwähnt wurde:
(*ptr)++
Das ist auch interessant. Wenn Sie haben:
char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
es wird Ihnen diese begeisterte Ausgabe geben:
HI
Was ist los? Auch hier geht es um Vorrang , Ausdruckswert und Nebenwirkungen . Aufgrund der Klammern wird der *p
Teil als primärer Ausdruck behandelt. Primäre Ausdrücke übertreffen alles andere; Sie werden zuerst ausgewertet. Und *p
, wie Sie wissen, bewertet zu 'H'
. Der Rest des Ausdrucks, der ++
Teil, wird auf diesen Wert angewendet. Also, in diesem Fall (*p)++
wird 'H'++
.
Was ist der Wert von 'H'++
? Wenn Sie gesagt haben 'I'
, haben Sie (bereits!) Unsere Diskussion über Wert vs. Nebenwirkung mit Postfix-Inkrement vergessen. Denken Sie daran, ergibt'H'++
den aktuellen Wert von 'H'
. Das wird also zuerst printf()
gedruckt 'H'
. Dann wurde als Nebeneffekt , dass 'H'
geht werden erhöht 'I'
. Der zweite printf()
druckt das 'I'
. Und du hast deinen fröhlichen Gruß.
In Ordnung, aber in den letzten beiden Fällen, warum brauche ich
char q[] = "Hello";
char* p = q;
Warum kann ich nicht einfach so etwas haben?
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
Weil "Hello"
ist ein String-Literal. Wenn Sie es versuchen ++*p
, versuchen Sie, 'H'
die Zeichenfolge in zu ändern 'I'
und die gesamte Zeichenfolge zu erstellen "Iello"
. In C sind String-Literale schreibgeschützt. Der Versuch, sie zu ändern, ruft undefiniertes Verhalten hervor. "Iello"
ist auch auf Englisch undefiniert, aber das ist nur Zufall.
Umgekehrt kann man nicht haben
char p[] = "Hello";
printf ("%c", *++p); // attempting to modify value of array identifier!
Warum nicht? Denn in diesem Fall p
handelt es sich um ein Array. Ein Array ist kein modifizierbarer l-Wert. Sie können nicht ändern, wo p
Punkte vor oder nach dem Inkrementieren oder Dekrementieren liegen, da der Name des Arrays so funktioniert, als wäre es ein konstanter Zeiger. (Das ist nicht das, was es eigentlich ist. Das ist nur eine bequeme Art, es zu betrachten.)
Zusammenfassend sind hier die drei Dinge, nach denen Sie gefragt haben:
*ptr++ // effectively dereferences the pointer, then increments the pointer
*++ptr // effectively increments the pointer, then dereferences the pointer
++*ptr // effectively dereferences the pointer, then increments dereferenced value
Und hier ist ein vierter, der genauso viel Spaß macht wie die anderen drei:
(*ptr)++ // effectively forces a dereference, then increments dereferenced value
Der erste und der zweite stürzen ab, wenn ptr
es sich tatsächlich um eine Array-ID handelt. Der dritte und vierte stürzen ab, wenn ptr
auf ein Zeichenfolgenliteral verwiesen wird.
Hier hast du es. Ich hoffe es ist jetzt alles Kristall. Sie waren ein großartiges Publikum und ich werde die ganze Woche hier sein.
(*ptr)++
(Klammern müssen*ptr++