it-swarm.com.de

Was ist der Unterschied zwischen char a [] =? String ?; und char * p =? string?;?

Wie die Überschrift sagt: Was ist der Unterschied zwischen 

char a[] = ?string?; and 
char *p = ?string?;  

Diese Frage wurde mir im Interview gestellt ... Ich verstehe die Aussage nicht.

char a[] = ?string?

Was ist der ? Operator? Ist es Teil einer Zeichenfolge oder hat es eine bestimmte Bedeutung?

47
Sachin Mhetre

Das erste ist ein Array, das andere ist ein Zeiger.

Die Array-Deklaration char a[6]; fordert, dass Platz für sechs Zeichen reserviert wird, der unter dem Namen a bekannt ist. Das heißt, es gibt einen Ort namens a, an dem sechs Zeichen Platz finden können. Die Zeigerdeklaration char *p; fordert dagegen eine Stelle an, die einen Zeiger enthält. Der Zeiger soll unter dem Namen p bekannt sein und kann irgendwo auf ein beliebiges Zeichen (oder ein zusammenhängendes Array von Zeichen) verweisen.

Die Aussagen

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

würde zu Datenstrukturen führen, die folgendermaßen dargestellt werden könnten:

     +---+---+---+---+---+---+----+
  a: | s | t | r | i | n | g | \0 |
     +---+---+---+---+---+---+----+
     +-----+     +---+---+---+---+---+---+---+ 
  p: |  *======> | s | t | r | i | n | g |\0 |    
     +-----+     +---+---+---+---+---+---+---+ 

Es ist wichtig zu wissen, dass eine Referenz wie x[3] anderen Code generiert, abhängig davon, ob x ein Array oder ein Zeiger ist. Wenn der Compiler den Ausdruck a[3] sieht, gibt er aufgrund der obigen Deklarationen Code aus, der an der Stelle a beginnt, drei Elemente an ihn vorbei bewegt und das Zeichen dort abruft. Wenn er den Ausdruck p[3] sieht, gibt er Code aus, der an der Position p beginnt, den Zeigerwert dort abruft, dem Zeiger drei Elementgrößen hinzufügt und schließlich das Zeichen abruft, auf das gezeigt wird. In dem obigen Beispiel ist a[3] und p[3] zufällig das Zeichen l, der Compiler wird jedoch anders angezeigt. 

Source: comp.lang.c FAQ Liste · Frage 6.2

84
user1208519

Der ? scheint ein Tippfehler zu sein, er ist nicht semantisch gültig. Die Antwort geht also davon aus, dass der ? ein Tippfehler ist und erklärt, was der Interviewer wahrscheinlich eigentlich fragen wollte.


Beide unterscheiden sich deutlich voneinander:

  1. Der erste erstellt einen Zeiger.
  2. Der zweite erstellt ein Array.

Lesen Sie weiter für eine detailliertere Erklärung:

Die Array-Version:

char a[] = "string";  

Erstellt ein Array, das groß genug ist, um das Zeichenfolgenliteral "string" einschließlich des NULL-Abschlusszeichens aufzunehmen. Das Array string wird mit dem String-Literal "string" initialisiert. Das Array kann zu einem späteren Zeitpunkt geändert werden. Die Größe des Arrays ist auch zur Kompilierzeit bekannt. Daher kann der Operator sizeof) verwendet werden, um seine Größe zu bestimmen. 


Die Zeigerversion:

char *p  = "string"; 

Erzeugt einen Zeiger, der auf einen String "String" zeigt. Dies ist schneller als die Array-Version die Zeichenfolge, auf die der Zeiger zeigt, sollte jedoch nicht geändert werden, da sie sich in einem schreibgeschützten implementierungsdefinierten Speicher befindet. Das Ändern eines solchen Zeichenfolgenlitals führt zu Undefined Behavior.

In der Tat veraltet C++ 03[Ref 1] Verwendung von String-Literal ohne das Schlüsselwort const. Die Deklaration sollte also lauten: 

const char *p = "string";

Außerdem müssen Sie Sie müssen die Funktion strlen() und nicht sizeof verwenden, um die Größe der Zeichenfolge zu ermitteln, da der Operator sizeof Ihnen nur die Größe der Zeigervariable angibt.


Welche Version ist besser und welche soll ich verwenden?

Abhängig von der Nutzung. 

  • Wenn Sie keine Änderungen an der Zeichenfolge vornehmen müssen, verwenden Sie die Zeigerversion. 
  • Wenn Sie die Daten ändern möchten, verwenden Sie die Array-Version.

Hinweis: Dies ist kein C++, aber dies ist C-spezifisch. 

Beachten Sie, dass die Verwendung von String-Literal ohne das Schlüsselwort const in C .. __ perfekt gültig ist. Das Ändern eines String-Literal ist jedoch immer noch ein undefiniertes Verhalten in C[Ref 2].

Dies wirft eine interessante Frage auf.
Was ist der Unterschied zwischen char * und const char *, wenn er mit String-Literalen in C verwendet wird?


Für Standerdese-Fans:
[Ref 1]C++ 03 Standard: §4.2/2 

Ein String-Literal (2.13.4), bei dem es sich nicht um ein Wide-String-Literal handelt, kann in einen R-Wert vom Typ „Zeiger auf Char“ konvertiert werden. Ein Wide-String-Literal kann in einen R-Wert vom Typ „Zeiger auf wchar_t“ konvertiert werden. In beiden Fällen ist das Ergebnis ein Zeiger auf das erste Element des Arrays. Diese Konvertierung wird nur berücksichtigt, wenn es einen explizit geeigneten Zeigerzieltyp gibt und nicht, wenn generell eine Konvertierung von einem l-Wert in einen r-Wert erforderlich ist. [Hinweis: Diese Konvertierung ist veraltet. Siehe Anhang D.] Für die Rangfolge der Überladungsauflösung (13.3.3.1.1) wird diese Konvertierung als Array-Zeiger-Konvertierung betrachtet, gefolgt von einer Qualifikationskonvertierung (4.4). [Beispiel: "abc" wird als Array-to-Pointer-Konvertierung in "Zeiger auf const-Char" und anschließend als Qualifikations-Konvertierung in "Zeiger auf Char" konvertiert. ]

C++ 11 entfernt einfach das obige Zitat, was impliziert, dass es sich um ungültigen Code in C++ 11 handelt.

[Ref 2]C99 Standard 6.4.5/5 "String Literals - Semantics": 

In Übersetzungsphase 7 wird ein Byte oder Code mit dem Wert Null an jede Multibyte-Zeichenfolge angehängt, die sich aus einem String-Literal oder Literalen ergibt. Die Multibyte-Zeichenfolge wird dann verwendet, um ein Array mit statischer Speicherdauer und Länge zu initialisieren, das gerade ausreicht, um die Folge zu enthalten. Für Zeichenkettenliterale haben die Array-Elemente den Typ char und werden mit den einzelnen Bytes der Multibyte-Zeichenfolge initialisiert. Bei Wide-String-Literalen haben die Array-Elemente den Typ wchar_t und werden mit der Folge von Wide-Zeichen initialisiert ...

Es ist nicht angegeben, ob diese Arrays verschieden sind, vorausgesetzt, ihre Elemente haben die entsprechenden Werte. Wenn das Programm versucht, ein solches Array zu ändern, ist das Verhalten undefiniert.

87
Alok Save
char a[] = "string";

Dadurch wird die Zeichenfolge auf dem Stapel zugewiesen.

char *p = "string";

Dadurch wird ein Zeiger auf dem Stapel erstellt, der auf das Literal im Datensegment des Prozesses zeigt.

? ist derjenige, der es geschrieben hat, der nicht weiß, was er tut.

Stack, Heap, Datasegment (und BSS) und Textsegment sind die vier Segmente des Prozessspeichers. Alle definierten lokalen Variablen befinden sich im Stapel. Dynmisch zugewiesener Speicher, der malloc und calloc verwendet, befindet sich im Heap. Alle globalen und statischen Variablen befinden sich im Datensegment. Das Textsegment enthält den Assembly-Code des Programms und einige Konstanten.

In diesen 4 Segmenten ist das Textsegment das READ ONLY-Segment und in den anderen drei Segmenten gilt READ und WRITE.

char a[] = "string"; - Dieses Statemnt reserviert Speicherplatz für 7 Byte im Stapel (wegen der lokalen Variablen) und behält alle 6 Zeichen (s, t, r, i, n, g) und das NULL-Zeichen (\0) am Ende bei.

char *p = "string"; - Diese Anweisung reserviert Speicher für 4 Bytes (wenn es sich um eine 32-Bit-Maschine handelt) im Stack (da dies auch eine lokale Variable ist) und enthält den Zeiger der konstanten Zeichenfolge, deren Wert "string" ist. Diese konstante Zeichenfolge von 6 Byte befindet sich im Textsegment. Dies ist ein konstanter Wert. Die Zeigervariable p zeigt nur auf diese Zeichenfolge. 

Nun bedeutet a[0] (Index kann 0 bis 5 sein), er greift auf das erste Zeichen der Zeichenfolge zu, die sich im Stapel befindet. Also können wir auch an dieser Stelle schreiben. a[0] = 'x'. Diese Operation ist zulässig, da wir READ WRITE Zugriff im Stack haben.

Aber p[0] = 'x' wird zum Absturz führen, da wir nur READ Zugriff auf das Textsegment haben. Segmentierungsfehler werden auftreten, wenn wir auf Textsegmente schreiben.

Sie können den Wert der Variablen p jedoch ändern, da sich die lokale Variable im Stapel befindet. Wie unten

char *p = "string";
printf("%s", p);
p = "start";
printf("%s", p);

Das ist erlaubt. Hier ändern wir die Adresse, die in der Zeigervariable p gespeichert ist, in die Adresse der Zeichenfolge start (wieder ist start auch schreibgeschützt). Wenn Sie die in *p vorhandenen Werte ändern möchten, gehen Sie für dynamisch zugewiesenen Speicher.

char *p = NULL;
p = malloc(sizeof(char)*7);
strcpy(p, "string");

Jetzt ist die p[0] = 'x'-Operation erlaubt, da jetzt in Heap geschrieben wird.

7
rashok

char *p = "string"; erstellt einen Zeiger auf den Nur-Lese-Speicher, in dem das Zeichenfolgenliteral "string" gespeichert ist. Der Versuch, eine Zeichenfolge zu ändern, auf die p verweist, führt zu undefiniertem Verhalten.

char a[] = "string"; erstellt ein Array und initialisiert seinen Inhalt mit dem String-Literal "string".

6
LihO

Sie unterscheiden sich jedoch darin, wo der Speicher gespeichert ist. Idealerweise sollte der zweite const char * verwenden.

Der erste 

char buf[] = "hello";

erstellt einen automatischen Puffer, der groß genug ist, um die Zeichen zu speichern, und kopiert sie (einschließlich des Nullabschlusszeichens).

Der zweite 

const char * buf = "hello";

sollte const verwenden und erstellt einfach einen Zeiger, der auf den Speicher verweist, der normalerweise im statischen Speicherbereich gespeichert ist und dessen Änderung nicht zulässig ist.

Das Gegenteil (der Tatsache, dass Sie den ersten sicher und nicht den zweiten modifizieren können) ist, dass der zweite Zeiger von einer Funktion zurückgegeben werden kann, nicht aber der erste. Dies liegt daran, dass der zweite ein gültiger Speicherzeiger außerhalb des Funktionsbereichs der Funktion bleibt, der erste jedoch nicht.

const char * sayHello()
{
     const char * buf = "hello";
     return buf; // valid
}

const char * sayHelloBroken()
{
     char buf[] = "hello";
     return buf; // invalid
}
3
CashCow

a deklariert ein Array von char-Werten - ein Array von chars, das beendet wird.

p deklariert einen Zeiger, der sich auf einen unveränderlichen, abgeschlossenen C-String bezieht, dessen genauer Speicherort implementierungsdefiniert ist. Beachten Sie, dass dies const-qualifiziert sein sollte (z. B. const char *p = "string";).

Wenn Sie es mit std::cout << "a: " << sizeof(a) << "\np: " << sizeof(p) << std::endl; ausdrucken, werden die Größenunterschiede angezeigt (Hinweis: Die Werte können je nach System variieren):

a: 7
p: 8

Was ist hier? Operator? Ist es Teil einer Zeichenfolge oder hat es eine bestimmte Bedeutung?

char a[] = ?string?

Ich gehe davon aus, dass es sich zuvor um doppelte Anführungszeichen "string" handelte, die möglicherweise in "intelligente Anführungszeichen" konvertiert wurden, dann jedoch nicht als solche dargestellt werden konnten und in ? konvertiert wurden.

1
justin

C und C++ haben sehr ähnliche Beziehungen zwischen Zeiger und Array ...

Ich kann nicht für die genauen Speicherorte der beiden Aussagen sprechen, nach denen Sie gefragt werden, aber ich fand die Artikel interessant und nützlich, um einige Unterschiede zwischen der char-Pointer-Deklaration und einer char-Array-Deklaration zu verstehen.

Zur Klarheit: 

C Zeiger und Array-Beziehung

C++ - Zeiger auf ein Array

Ich denke, es ist wichtig zu wissen, dass ein Array in C und C++ ein konstanter Zeiger auf das erste Element des Arrays ist. Folglich können Sie eine Zeigerarithmetik für das Array ausführen. 

char * p = "string"; <--- Dies ist ein Zeiger, der auf die erste Adresse einer Zeichenfolge zeigt. 

folgendes ist auch möglich:

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

p = a; 

P bezieht sich jetzt auf die erste Speicheradresse von a (die Adresse des ersten Elements)

und so * p == 's'

* (p ++) == 't' und so weiter. (oder * (p + 1) == 't')

und dasselbe würde für a funktionieren: * (a ++) oder * (a + 1) würden auch gleich 't' sein

0
meltdownmonk