wake-up-neo.net

Was ist der Unterschied zwischen char array und char pointer in C?

Ich versuche, Zeiger in C zu verstehen, aber ich verwechsle derzeit Folgendes:

  • char *p = "hello"
    

    Dies ist ein Zeichenzeiger, der auf das Zeichenfeld zeigt, beginnend mit h.

  • char p[] = "hello"
    

    Dies ist ein Array, das hallo speichert.

Was ist der Unterschied, wenn ich diese beiden Variablen in diese Funktion übergebe?

void printSomething(char *p)
{
    printf("p: %s",p);
}
195
diesel

char* und char[]sind verschiedene Typen, aber es ist nicht in allen Fällen sofort ersichtlich. Dies liegt daran, dass Arrays in Zeiger zerfallen, dh wenn ein Ausdruck vom Typ char[] wird angegeben, wenn einer vom Typ char* wird erwartet, der Compiler konvertiert 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:

char s[10] = "hello";
printSomething(s);

Der Compiler gibt vor, dass Sie Folgendes geschrieben haben:

char s[10] = "hello";
printSomething(&s[0]);
207
Jon

Mal sehen:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

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 in der Norm. Und auf der Ebene der Laufzeit kein Unterschied zwischen ihnen (in Assembler, na ja, fast, siehe unten).

Es gibt auch eine verwandte Frage in der C FAQ :

[~ # ~] q [~ # ~] : Was ist der Unterschied zwischen diesen Initialisierungen?

char a[] = "string literal";   
char *p  = "string literal";   

Mein Programm stürzt ab, wenn ich versuche, p [i] einen neuen Wert zuzuweisen.

[~ # ~] a [~ # ~] : Ein Zeichenfolgenliteral (der formale Begriff für eine Zeichenfolge in Anführungszeichen in C-Quelle) kann verwendet werden auf zwei leicht unterschiedliche Arten:

  1. Als Initialisierer für ein Array von char gibt es, wie in der Deklaration von char a [], die Anfangswerte der Zeichen in diesem Array an (und gegebenenfalls dessen Größe).
  2. An jedem anderen Ort wird es zu einem unbenannten, statischen Array von Zeichen, und dieses unbenannte Array kann im Nur-Lese-Speicher gespeichert werden und kann daher nicht notwendigerweise geändert werden. In einem Ausdruckskontext wird das Array wie gewohnt sofort in einen Zeiger konvertiert (siehe Abschnitt 6), sodass die zweite Deklaration p so initialisiert, dass es auf das erste Element des unbenannten Arrays verweist.

Einige Compiler verfügen über einen Schalter, der steuert, ob Zeichenfolgenliterale schreibbar sind oder nicht (um alten Code zu kompilieren), und einige haben möglicherweise Optionen, um zu bewirken, dass Zeichenfolgenliterale formal als Arrays von const char behandelt werden (um Fehler besser abfangen zu können).

Siehe auch Fragen 1.31, 6.1, 6.2, 6.8 und 11.8b.

Referenzen: K & R2 Sec. 5,5 p. 104

ISO Sec. 6.1.4, Ziff. 6.5.7

Begründung Sec. 3.1.4

H & S Sec. 2.7.4 S. 31-2

75
JJJ

Was ist der Unterschied zwischen char array und char pointer in C?

C99 N1256 Entwurf

Es gibt zwei verschiedene Verwendungen von Zeichenkettenliteralen:

  1. Initialisiere char[]:

    char c[] = "abc";      
    

    Dies ist "mehr Magie" und wird unter 6.7.8/14 "Initialisierung" beschrieben:

    Ein Array mit einem Zeichentyp kann durch ein Zeichenkettenliteral initialisiert werden, das optional in geschweiften Klammern eingeschlossen ist. Aufeinanderfolgende Zeichen des Zeichenfolgenliteral (einschließlich des abschließenden Nullzeichens, wenn Platz vorhanden ist oder das Array eine unbekannte Größe hat) initialisieren die Elemente des Arrays.

    Das ist also nur eine Abkürzung für:

    char c[] = {'a', 'b', 'c', '\0'};
    

    Wie jedes andere reguläre Array kann c geändert werden.

  2. Überall sonst: es erzeugt ein:

    Wenn Sie also schreiben:

    char *c = "abc";
    

    Dies ist ähnlich wie:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Beachten Sie die implizite Besetzung von char[] bis char *, was immer legal ist.

    Wenn Sie dann c[0], Sie ändern auch __unnamed, das ist UB.

    Dies ist in 6.4.5 "String-Literale" dokumentiert:

    5 In der Übersetzungsphase 7 wird an jede Multibyte-Zeichenfolge, die sich aus einem String-Literal oder Literalen ergibt, ein Byte oder ein Code mit dem Wert Null angehängt. Die Multibyte-Zeichenfolge wird dann verwendet, um ein Array mit statischer Speicherdauer und -länge zu initialisieren, die gerade ausreichen, um die Folge zu enthalten. Bei Zeichenkettenliteralen haben die Array-Elemente den Typ char und werden mit den einzelnen Bytes der Multibyte-Zeichenfolge [...] initialisiert.

    6 Es ist nicht festgelegt, ob diese Arrays unterschiedlich sind, sofern ihre Elemente die entsprechenden Werte haben. Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.

6.7.8/32 "Initialisierung" gibt ein direktes Beispiel:

BEISPIEL 8: Die Deklaration

char s[] = "abc", t[3] = "abc";

definiert "einfache" Zeichen-Array-Objekte s und t, deren Elemente mit Zeichenketten-Literalen initialisiert werden.

Diese Erklärung ist identisch mit

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

Der Inhalt der Arrays kann geändert werden. Auf der anderen Seite die Erklärung

char *p = "abc";

definiert p mit dem Typ "pointer to char" und initialisiert es so, dass es auf ein Objekt mit dem Typ "array of char" mit der Länge 4 verweist, dessen Elemente mit einem Zeichenkettenliteral initialisiert werden. Wenn versucht wird, mit p den Inhalt des Arrays zu ändern, ist das Verhalten undefiniert.

GCC 4.8 x86-64 ELF-Implementierung

Programm:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Kompilieren und dekompilieren:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Die Ausgabe enthält:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Fazit: GCC speichert char* es in .rodata Abschnitt, nicht in .text.

Wenn wir dasselbe tun für char[]:

 char s[] = "abc";

wir erhalten:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

so wird es im Stapel gespeichert (relativ zu %rbp).

Beachten Sie jedoch, dass das Standard-Linker-Skript .rodata und .text im selben Segment, das ausgeführt wurde, aber keine Schreibberechtigung hat. Dies kann beobachtet werden mit:

readelf -l a.out

was beinhaltet:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

Sie dürfen den Inhalt einer String-Konstante nicht ändern, worauf das erste p zeigt. Das zweite p ist ein Array, das mit einer Zeichenfolgenkonstante initialisiert wird, und Sie können dessen Inhalt ändern.

9
potrzebie

In solchen Fällen ist der Effekt der gleiche: Sie übergeben die Adresse des ersten Zeichens in einer Zeichenfolge.

Die Erklärungen sind natürlich nicht die gleichen.

Im Folgenden wird Speicher für eine Zeichenfolge und einen Zeichenzeiger reserviert und der Zeiger so initialisiert, dass er auf das erste Zeichen in der Zeichenfolge verweist.

char *p = "hello";

Während das Folgende Speicher nur für die Zeichenfolge reserviert. Es kann also tatsächlich weniger Speicher verbrauchen.

char p[10] = "hello";
6
Jonathan Wood

Soweit ich mich erinnern kann, ist ein Array eine Gruppe von Zeigern. Zum Beispiel

p[1]== *(&p+1)

ist eine wahre Aussage

3
CosminO

Aus APUE , Abschnitt 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

... Für die erste Vorlage wird der Name auf dem Stack vergeben, da wir eine Array-Variable verwenden. Für den zweiten Namen verwenden wir jedoch einen Zeiger. In diesem Fall befindet sich nur der Speicher für den Zeiger selbst auf dem Stapel. Der Compiler sorgt dafür, dass die Zeichenfolge im schreibgeschützten Segment der ausführbaren Datei gespeichert wird. Wenn die Funktion mkstemp versucht, die Zeichenfolge zu ändern, tritt ein Segmentierungsfehler auf.

Der zitierte Text entspricht der Erklärung von @Ciro Santilli.

2
Rick

char p[3] = "hello"? sollte sein char p[6] = "hello" Denken Sie 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 liegen in der Semantik. Während Sie den Wert eines Zeigers so ändern können, dass er auf eine andere Stelle im Speicher zeigt, zeigt ein Array nach seiner Erstellung immer auf dieselbe Stelle.
Auch bei Verwendung des Arrays wird das "Neu" und "Löschen" automatisch für Sie erledigt.

1
Roee Gavirel