Was ist der Unterschied zwischen Char-Array und Char-Zeiger in C?

Lesezeit: 10 Minuten

Benutzer-Avatar von diesel
Diesel-

Ich versuche, Zeiger in C zu verstehen, aber ich bin derzeit mit Folgendem verwirrt:

  • char *p = "hello"
    

    Dies ist ein char-Zeiger, der auf das Zeichenarray zeigt, beginnend bei h.

  • char p[] = "hello"
    

    Dies ist ein Array, das speichert hallo.

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

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

  • Dies wäre nicht gültig: char p[3] = "hello"; Die Initialisierungszeichenfolge ist zu lang für die Größe des deklarierten Arrays. Tippfehler?

    – Cody Grey

    17. April 2012 um 7:17 Uhr

  • Oder nur char p[]="hello"; würde genügen!

    – Tieftauchgang

    2. Juli 2014 um 10:03 Uhr

  • mögliches Duplikat von C: Unterschiede zwischen Zeichenzeiger und Array

    – Saschoalm

    1. Februar 2015 um 18:27 Uhr

  • mögliches Duplikat von Was ist der Unterschied zwischen char s[] und char *s in C? Dies fragt zwar auch speziell nach dem Funktionsparameter, aber das ist es nicht char Spezifisch.

    – Ciro Santilli OurBigBook.com

    5. Juni 2015 um 7:44 Uhr

  • Sie müssen verstehen, dass sie sich grundlegend unterscheiden. Die einzige Gemeinsamkeit dabei ist, dass die Basis des Arry p[] ist ein konstanter Zeiger, der den Zugriff auf das Array p ermöglicht[] über einen Zeiger. p[] selbst enthält Speicher für eine Zeichenfolge, während *p nur auf die Adresse des ersten Elements von nur EINEM ZEICHEN zeigt (dh auf die Basis einer bereits zugewiesenen Zeichenfolge). Um dies besser zu veranschaulichen, betrachten Sie Folgendes: char *cPtr = {‘h’,’e’,’l’,’l’,’o’, ‘\0’}; ==>Dies ist ein Fehler, da cPtr ein Zeiger auf nur ein Zeichen char cBuff ist[] = {‘h’, ‘e’,’l’,’l’,’o’,’\0′}; ==>Das ist in Ordnung, bcos cBuff selbst ist ein char-Array

    – Ilavarasan

    27. Oktober 2015 um 3:20 Uhr


Jons Benutzeravatar
Jon

char* und char[] sind verschiedene Arten, aber es ist nicht in allen Fällen sofort ersichtlich. Dies liegt daran, dass Arrays zerfallen in Zeigerwas bedeutet, dass wenn ein Ausdruck vom Typ char[] vorgesehen ist, wo eine vom Typ char* erwartet wird, wandelt der Compiler das Array automatisch in einen Zeiger auf sein erstes Element um.

Ihre Beispielfunktion printSomething erwartet einen Zeiger, wenn Sie also versuchen, ihm 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]);

  • Hat sich von 2012 bis heute etwas geändert. Für ein Zeichenarray druckt “s” das gesamte Array, dh “hello”

    – Bhanu Tez

    9. Mai 2019 um 6:48 Uhr

  • @BhanuTez Nein, wie Daten gespeichert werden und was mit den Daten gemacht wird, sind separate Anliegen. Dieses Beispiel gibt die gesamte Zeichenfolge aus, da dies so ist printf behandelt die %s Format-String: Beginnen Sie an der angegebenen Adresse und fahren Sie fort, bis Sie auf ein Null-Terminator stoßen. Wenn Sie nur ein Zeichen drucken möchten, können Sie die verwenden %c Format-String, zum Beispiel.

    – Jacobq

    17. Juni 2019 um 10:59 Uhr

  • Wollte nur fragen ob char *p = "abc"; das NULL-Zeichen \0 wird wie bei char automatisch angehängt [] Reihe?

    – ajaysinghnegi

    8. Juli 2019 um 17:53 Uhr

  • warum ich einstellen kann char *name; name="123"; kann aber das gleiche mit machen int Typ? Und nach dem Gebrauch %c zu drucken name die Ausgabe ist eine unlesbare Zeichenfolge: ?

    – Tom Sawyer

    23. April 2020 um 19:52 Uhr

Benutzeravatar von JJJ
JJJ

Mal schauen:

#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, sofern bekannt, z. B. wenn das Array statisch zugewiesen ist), können die Details sein in der Norm gefunden. Und auf der Ebene der Laufzeit gibt es keinen Unterschied zwischen ihnen (in Assembler, naja, fast, siehe unten).

Außerdem gibt es eine verwandte Frage in dem C-Häufig gestellte Fragen:

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 einen neuen Wert zuzuweisen[i].

EIN: Ein Zeichenfolgenliteral (der formale Begriff für eine Zeichenfolge in doppelten Anführungszeichen in C-Quellen) kann auf zwei leicht unterschiedliche Arten verwendet werden:

  1. Als Initialisierer für ein Array von char, wie in der Deklaration von char a[] gibt es die Anfangswerte der Zeichen in diesem Array an (und, falls erforderlich, seine Größe).
  2. Überall sonst wird es zu einem unbenannten, statischen Array von Zeichen, und dieses unbenannte Array kann im Nur-Lese-Speicher gespeichert werden und kann daher nicht unbedingt geändert werden. In einem Ausdruckskontext wird das Array wie üblich sofort in einen Zeiger umgewandelt (siehe Abschnitt 6), sodass die zweite Deklaration p so initialisiert, dass es auf das erste Element des unbenannten Arrays zeigt.

Einige Compiler haben einen Schalter, der steuert, ob String-Literale schreibbar sind oder nicht (zum Kompilieren von altem Code), und einige haben möglicherweise Optionen, um zu bewirken, dass String-Literale formal als Arrays von const char behandelt werden (für eine bessere Fehlererkennung).

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

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

ISO-Sek. 6.1.4, Kap. 6.5.7

Begründung Sek. 3.1.4

H&S Sek. 2.7.4 S. 31-2

  • Warum zerfällt q in sizeof(q) nicht in einen Zeiger, wie @Jon in seiner Antwort erwähnt?

    – garp

    21. April 2016 um 19:23 Uhr

  • @garyp q zerfällt nicht in einen Zeiger, weil sizeof ein Operator und keine Funktion ist (selbst wenn sizeof eine Funktion wäre, würde q nur zerfallen, wenn die Funktion einen char -Zeiger erwartet).

    – GiriB

    14. August 2016 um 17:07 Uhr

  • danke, aber printf(“%u\n” statt printf(“%zu\n” , ich denke du solltest z entfernen.

    – Zakaria

    17. Februar 2018 um 20:11 Uhr

Ciro Santilli Benutzeravatar von OurBigBook.com
Ciro Santilli OurBigBook.com

Was ist der Unterschied zwischen Char-Array und Char-Zeiger in C?

C99 N1256-Entwurf

Es gibt zwei verschiedene Verwendungen von Zeichenfolgenliteralen:

  1. Initialisieren char[]:

    char c[] = "abc";      
    

    Dies ist “mehr Magie” und unter 6.7.8/14 “Initialisierung” beschrieben:

    Ein Array vom Zeichentyp kann durch ein Zeichenfolgenliteral initialisiert werden, das optional in geschweiften Klammern eingeschlossen ist. Aufeinanderfolgende Zeichen des Zeichenfolgenliterals (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, c können geändert werden.

  2. Überall sonst: es erzeugt ein:

    • unbenannt
    • array of char Was ist der Typ von String-Literalen in C und C++?
    • mit statischer Lagerung
    • das gibt UB (undefiniertes Verhalten), wenn es modifiziert wird

    Also wenn du schreibst:

    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 Umwandlung von char[] zu char *was immer legal ist.

    Dann, wenn Sie ändern c[0]ändern Sie auch __unnameddas ist UB.

    Dies ist unter 6.4.5 “String-Literale” dokumentiert:

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

    6 Es ist nicht spezifiziert, ob diese Arrays unterschiedlich sind, vorausgesetzt, ihre Elemente haben die entsprechenden Werte. 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” Char-Array-Objekte s und t deren Elemente mit Zeichenkettenliteralen initialisiert werden.

Diese Deklaration ist identisch mit

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

Der Inhalt der Arrays ist modifizierbar. Andererseits die Deklaration

char *p = "abc";

definiert p vom Typ „pointer to char“ und initialisiert es so, dass es auf ein Objekt vom Typ „array of char“ der Länge 4 zeigt, dessen Elemente mit einem Zeichenfolgenliteral initialisiert werden. Wenn versucht wird, zu verwenden p um 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

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-Stores char* es hinein .rodata Abschnitt, nicht drin .text.

Wenn wir dasselbe für tun char[]:

 char s[] = "abc";

wir erhalten:

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

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

Beachten Sie jedoch, dass das standardmäßige Linker-Skript puts .rodata und .text im selben Segment, das Ausführungs-, aber keine Schreibberechtigung hat. Dies kann beobachtet werden mit:

readelf -l a.out

was beinhaltet:

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

  • @leszek.hanusz Undefiniertes Verhalten stackoverflow.com/questions/2766731/… Google “C-Sprache UB” 😉

    – Ciro Santilli OurBigBook.com

    3. Mai 2016 um 9:47 Uhr

Benutzeravatar von potrzebie
potrzebie

Sie dürfen den Inhalt einer String-Konstante nicht ändern, was die erste ist p verweist auf. Der Zweite p ist ein Array, das mit einer String-Konstante initialisiert wird, und Sie kann seinen Inhalt ändern.

Benutzeravatar von Jonathan Wood
Jonathan Holz

In solchen Fällen ist der Effekt derselbe: Sie übergeben am Ende die Adresse des ersten Zeichens in einer Zeichenkette.

Die Deklarationen sind jedoch offensichtlich nicht gleich.

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

char *p = "hello";

Während im Folgenden Speicher nur für die Zeichenfolge reserviert wird. Es kann also tatsächlich weniger Speicher verbrauchen.

char p[10] = "hello";

  • codeplusplus.blogspot.com/2007/09/… “Das Initialisieren der Variablen erfordert jedoch eine enorme Leistungs- und Platzeinbuße für das Array.”

    – Blatt

    10. März 2013 um 14:22 Uhr

  • @leef: Ich denke, das hängt davon ab, wo sich die Variable befindet. Wenn es sich im statischen Speicher befindet, ist es meiner Meinung nach möglich, dass das Array und die Daten im EXE-Image gespeichert werden und überhaupt keine Initialisierung erforderlich ist. Ansonsten ja, es kann sicher langsamer werden, wenn die Daten allokiert werden müssen und dann die statischen Daten hineinkopiert werden müssen.

    – Jonathan Holz

    31. März 2015 um 15:34 Uhr

Benutzeravatar von Rick
Rick

Aus APUEAbschnitt 5.14 :

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

… Beim ersten Template 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 der mkstemp -Funktion versucht, die Zeichenfolge zu ändern, tritt ein Segmentierungsfehler auf.

Der zitierte Text stimmt mit der Erklärung von @Ciro Santilli überein.

  • codeplusplus.blogspot.com/2007/09/… “Das Initialisieren der Variablen erfordert jedoch eine enorme Leistungs- und Platzeinbuße für das Array.”

    – Blatt

    10. März 2013 um 14:22 Uhr

  • @leef: Ich denke, das hängt davon ab, wo sich die Variable befindet. Wenn es sich im statischen Speicher befindet, ist es meiner Meinung nach möglich, dass das Array und die Daten im EXE-Image gespeichert werden und überhaupt keine Initialisierung erforderlich ist. Ansonsten ja, es kann sicher langsamer werden, wenn die Daten allokiert werden müssen und dann die statischen Daten hineinkopiert werden müssen.

    – Jonathan Holz

    31. März 2015 um 15:34 Uhr

Benutzeravatar von CosminO
KosminO

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

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

ist eine wahre aussage

  • Ich würde ein Array als einen Zeiger auf die Adresse eines Speicherblocks beschreiben. Daher warum *(arr + 1) bringt Sie zum zweiten Mitglied von arr. Wenn *(arr) zeigt auf eine 32-Bit-Speicheradresse, z bfbcdf5edann *(arr + 1) verweist auf bfbcdf60 (das zweite Byte). Daher führt das Verlassen des Bereichs eines Arrays zu seltsamen Ergebnissen, wenn das Betriebssystem keinen Segfault ausführt. Wenn int a = 24; ist unter Adresse bfbcdf62dann zugreifen arr[2] könnte zurückkehren 24vorausgesetzt, es tritt nicht zuerst ein Segfault auf.

    – Braden Best

    5. Februar 2014 um 3:05 Uhr


1426510cookie-checkWas ist der Unterschied zwischen Char-Array und Char-Zeiger in C?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy