Ich versuche, Zeiger in C zu verstehen, bin aber derzeit mit Folgendem verwechselt:
char *p = "hello"
Dies ist ein Zeichenzeiger, der auf das Zeichenarray zeigt, beginnend bei h .
char p[] = "hello"
Dies ist ein Array, das Hallo speichert .
Was ist der Unterschied, wenn ich beide Variablen an diese Funktion übergebe?
void printSomething(char *p)
{
printf("p: %s",p);
}
char p[3] = "hello";
Die Initialisierungszeichenfolge ist zu lang für die Größe des von Ihnen deklarierten Arrays. Tippfehler?char p[]="hello";
würde einfach ausreichen!char
spezifisch.Antworten:
char*
undchar[]
sind verschiedene Typen , aber es ist nicht in allen Fällen sofort ersichtlich. Dies liegt daran, dass Arrays in Zeiger zerfallen. Wenn also ein Ausdruck vom Typchar[]
bereitgestellt wird, bei dem einer vom Typchar*
erwartet wird, konvertiert der Compiler das Array automatisch in einen Zeiger auf sein erstes Element.Ihre Beispielfunktion
printSomething
erwartet einen Zeiger. Wenn Sie also versuchen, ein Array wie folgt zu übergeben:Der Compiler gibt vor, Folgendes geschrieben zu haben:
quelle
printf
die%s
Formatzeichenfolge behandelt wird: Beginnen Sie an der angegebenen Adresse und fahren Sie fort, bis Sie auf den Nullterminator stoßen. Wenn Sie nur ein Zeichen drucken möchten, können Sie beispielsweise die Formatzeichenfolge verwenden%c
.char *p = "abc";
das NULL-Zeichen\0
automatisch angehängt wird, wie im Fall eines char [] -Arrays?char *name; name="123";
aber kann das gleiche mitint
Typ tun ? Und nach%c
dem Druckenname
ist die Ausgabe eine unlesbare Zeichenfolge :�
?Mal schauen:
foo * und foo [] sind unterschiedliche Typen und werden vom Compiler unterschiedlich behandelt (Zeiger = Adresse + Darstellung des Zeigertyps, Array = Zeiger + optionale Länge des Arrays, falls bekannt, z. B. wenn das Array statisch zugeordnet ist ) finden Sie die Details im Standard. Und auf der Ebene der Laufzeit kein Unterschied zwischen ihnen (im Assembler, na ja, fast, siehe unten).
Es gibt auch eine verwandte Frage in den C-FAQ :
quelle
C99 N1256 Entwurf
Es gibt zwei verschiedene Verwendungen von Zeichenkettenliteralen:
Initialisieren
char[]
:Dies ist "mehr Magie" und wird unter 6.7.8 / 14 "Initialisierung" beschrieben:
Dies ist also nur eine Abkürzung für:
c
Kann wie jedes andere reguläre Array geändert werden.Überall sonst: es erzeugt ein:
Also, wenn Sie schreiben:
Dies ist ähnlich wie:
Beachten Sie die implizite Besetzung von
char[]
bischar *
, die immer legal ist.Wenn Sie dann ändern
c[0]
, ändern Sie auch__unnamed
, was UB ist.Dies ist unter 6.4.5 "String-Literale" dokumentiert:
6.7.8 / 32 "Initialisierung" gibt ein direktes Beispiel:
GCC 4.8 x86-64 ELF-Implementierung
Programm:
Kompilieren und dekompilieren:
Die Ausgabe enthält:
Fazit: GCC speichert
char*
es in.rodata
Abschnitt, nicht in.text
.Wenn wir dasselbe tun für
char[]
:wir erhalten:
so wird es im Stapel gespeichert (relativ zu
%rbp
).Beachten Sie jedoch , dass der Standard Linker - Skript setzt
.rodata
und.text
im gleichen Segment, das auszuführen hat , aber keine Schreibrechte. Dies kann beobachtet werden mit:was beinhaltet:
quelle
Sie dürfen den Inhalt einer Zeichenfolgenkonstante nicht ändern, worauf der erste
p
Punkt hinweist. Das zweitep
ist ein Array, das mit einer Zeichenfolgenkonstante initialisiert wurde, und Sie können dessen Inhalt ändern.quelle
In solchen Fällen ist der Effekt derselbe: Am Ende übergeben Sie die Adresse des ersten Zeichens in einer Zeichenfolge.
Die Erklärungen sind jedoch offensichtlich nicht dieselben.
Im Folgenden wird der Speicher für eine Zeichenfolge und auch einen Zeichenzeiger reserviert und der Zeiger so initialisiert, dass er auf das erste Zeichen in der Zeichenfolge zeigt.
Während das Folgende Speicher nur für die Zeichenfolge reserviert. Es kann also tatsächlich weniger Speicher verbrauchen.
quelle
Soweit ich mich erinnern kann, ist ein Array tatsächlich eine Gruppe von Zeigern. Beispielsweise
ist eine wahre Aussage
quelle
*(arr + 1)
bringt Sie zum zweiten Mitglied vonarr
. Wenn*(arr)
Punkte auf eine 32-Bit - Speicheradresse, zum Beispielbfbcdf5e
, dann*(arr + 1)
weisen aufbfbcdf60
(das zweite Byte). Daher führt das Verlassen des Bereichs eines Arrays zu seltsamen Ergebnissen, wenn das Betriebssystem keinen Fehler aufweist. Wennint a = 24;
ist an der Adressebfbcdf62
, wird der Zugriffarr[2]
möglicherweise zurückgegeben24
, vorausgesetzt, ein Segfault tritt nicht zuerst auf.Aus APUE , Abschnitt 5.14 :
Der zitierte Text entspricht der Erklärung von @Ciro Santilli.
quelle
char p[3] = "hello"
? Denken Siechar p[6] = "hello"
daran, dass am Ende eines "Strings" in C ein '\ 0'-Zeichen steht.Auf jeden Fall ist das Array in C nur ein Zeiger auf das erste Objekt eines Anpassungsobjekts im Speicher. Die einzigen Unterschiede sind in der Semantik. Während Sie den Wert eines Zeigers so ändern können, dass er auf eine andere Position im Speicher zeigt, zeigt ein Array nach seiner Erstellung immer auf dieselbe Position.
Auch bei Verwendung des Arrays werden "Neu" und "Löschen" automatisch für Sie erledigt.
quelle