web-dev-qa-db-ger.com

Was ist der Unterschied zwischen char * str = {"foo", ...} und char str [] [5] = {"foo", ...} Arraydefinitionen?

Fall 1: Wenn ich schreibe

char*str={"what","is","this"};

dann ist str[i]="newstring"; gültig, wohingegen str[i][j]='j'; ungültig ist.

Fall 2: Wenn ich schreibe

char str[][5]={"what","is","this"};

dann ist str[i]="newstring"; nicht gültig, wohingegen str[i][j]='J'; gültig ist.

Wieso ist es so? Ich bin ein Anfänger, der nach dem Lesen der anderen Antworten schon sehr verwirrt ist.

30
Ashish Dogra

Zuallererst: Ein Vorschlag: Bitte lesen Sie über Arrays sind keine Zeiger und umgekehrt !!

Das heißt, um dieses besondere Szenario aufzuklären, 

  • Im ersten Fall

    char*str={"what","is","this"};
    

    tut nicht das, was Sie denken. Es handelt sich um einen Verstoß gegen eine Einschränkung, der eine Diagnose von einer konformen C-Implementierung gemäß Kapitel 6.7.9/P2 erfordert:

    Kein Initialisierer darf versuchen, einen Wert für ein Objekt anzugeben, das nicht in der Entität enthalten ist initialisiert werden.

    Wenn Sie Warnungen aktivieren, sehen Sie (mindestens)

    warnung: Übermäßige Elemente im Skalar-Initialisierer

      char*str={"what","is","this"};
    

    Der Compiler a(ny) mit strenger Übereinstimmung muss jedoch den Code nicht kompilieren. Für den Fall, dass der Compiler ohnehin die Kompilierung und Erstellung einer Binärdatei gewählt hat, liegt das Verhalten nicht im Definitionsbereich der C-Sprache, es liegt an der Implementierung des Compilers (und kann daher stark variieren).

    In diesem Fall entschied der Compiler, dass diese Anweisung funktional nur char*str= "what"; ist.

    Hier ist str ein Zeiger auf eine char, die auf ein String-Literal zeigt. Sie können dem Zeiger erneut zuweisen. 

    str="newstring";  //this is valid
    

    aber eine Aussage wie

     str[i]="newstring";
    

    wäre ungültig, da hier versucht wird, einen Zeigertyp zu konvertieren und in einen char-Typ zu speichern, wobei die Typen nicht kompatibel sind. Der Compiler sollte in diesem Fall eine Warnung über die ungültige Konvertierung ausgeben.

    Danach eine aussage wie

    str[i][j]='J'; // compiler error
    

    ist syntaktisch ungültig, da Sie den Operator [] für die Array-Subskription für etwas verwenden, das nicht "Zeiger auf vollständigen Objekttyp" ist, wie z

    str[i][j] = ...
          ^^^------------------- cannot use this
    ^^^^^^ --------------------- str[i] is of type 'char', 
                                 not a pointer to be used as the operand for [] operator.
    
  • Andererseits im zweiten Fall

    str ist ein Array von Arrays. Sie können einzelne Array-Elemente ändern,

     str[i][j]='J'; // change individual element, good to go.
    

    sie können jedoch keinem Array zuordnen.

     str[i]="newstring";  // nopes, array type is not an lvalue!!
    

  • Schließlichin Anbetracht dessen, dass Sie schreiben wollten (wie in Kommentaren zu sehen ist)

    char* str[ ] ={"what","is","this"};
    

    in Ihrem ersten Fall gilt die gleiche Logik für Arrays. Dies macht str zu einem Array von Zeigern. Die Array-Mitglieder sind also zuweisbar.

    str[i]="newstring";  // just overwrites the previous pointer
    

    ist vollkommen in Ordnung. Die Zeiger, die als Array-Mitglieder gespeichert sind, sind jedoch Zeiger auf String-Literal. Aus demselben Grund rufen Sie undefined behaviour auf, wenn Sie einen der ändern wollen Elemente des Speichers, die zum String-Literal gehören

     str[i][j]='j';   //still invalid, as above.
    
26
Sourav Ghosh

Das Speicherlayout ist anders:

char* str[] = {"what", "is", "this"};

    str
+--------+      +-----+
| pointer| ---> |what0|
+--------+      +-----+   +---+
| pointer| -------------> |is0|
+--------+                +---+    +-----+
| pointer| ----------------------> |this0|
+--------+                         +-----+

In diesem Speicherlayout ist str ein Array von Zeigern auf die einzelnen Zeichenfolgen. Normalerweise befinden sich diese einzelnen Zeichenfolgen im statischen Speicher, und es ist ein Fehler, sie zu ändern. In der Grafik habe ich 0 verwendet, um die abschließenden Nullbytes zu bezeichnen.

char str[][5] = {"what", "is", "this"};

  str
+-----+
|what0|
+-----+
|is000|
+-----+
|this0|
+-----+

In diesem Fall ist str ein zusammenhängendes 2D-Array von Zeichen, das sich auf dem Stapel befindet. Die Zeichenfolgen werden in diesen Speicherbereich kopiert, wenn das Array initialisiert wird, und die einzelnen Zeichenfolgen werden mit null Bytes aufgefüllt, um dem Array eine reguläre Form zu geben.

Diese beiden Speicherlayouts sind grundsätzlich nicht miteinander kompatibel. Sie können auch keine Funktion übergeben, die einen Zeiger auf die andere erwartet. Der Zugriff auf die einzelnen Zeichenketten ist jedoch kompatibel. Wenn Sie str[1] schreiben, erhalten Sie einen char* für das erste Zeichen eines Speicherbereichs, der die Bytes is0 enthält, d. H. Eine C-Zeichenfolge.

Im ersten Fall ist es klar, dass dieser Zeiger einfach aus dem Speicher geladen wird. Im zweiten Fall wird der Zeiger über Array-Pointer-Decay erstellt: str[1] bezeichnet tatsächlich ein Array mit genau fünf Bytes (is000), das in fast allen Kontexten sofort in einen Zeiger auf sein erstes Element zerfällt. Ich glaube jedoch, dass eine vollständige Erklärung des Array-Pointer-Decays den Rahmen dieser Antwort sprengen würde. Google-Array-Pointer-Decay, wenn Sie neugierig sind.

17
cmaster

Mit der ersten definieren Sie eine Variable, die einen Zeiger auf eine char darstellt, die normalerweise nur als single-String verwendet wird. Der Zeiger wird so initialisiert, dass er auf das Zeichenfolgenliteral "what" zeigt. Der Compiler sollte sich auch darüber beklagen, dass Sie zu viele Initialisierer in der Liste haben.

Die zweite Definition macht str zu einem Array von drei Arrays von fünf char. Das heißt, es handelt sich um ein Array von drei Zeichenfolgen mit fünf Zeichen.


Etwas anders kann man so etwas sehen:

Für den ersten Fall:

 + ----- + + -------- + 
 | str | -> | "was" | 
 + ----- + + -------- + 

Und zum zweiten hast du

 + -------- + -------- + -------- + 
 | "was" | "ist" | "this" | 
 + -------- + -------- + -------- + 

Beachten Sie außerdem, dass bei der ersten Version mit dem Zeiger auf einen einzelnen String der Ausdruck str[i] = "newstring" auch zu Warnungen führen sollte, wenn Sie versuchen, einen Zeiger auf dassingle char-Elementstr[i] zuzuweisen.

Diese Zuweisung ist auch in der zweiten Version ungültig, aber aus einem anderen Grund: str[i] ist ein array (von fünf char-Elementen), und Sie können einem Array keine Zuweisungen zuordnen, sondern nur kopieren. Sie könnten also try mit strcpy(str[i], "newstring") machen und der Compiler wird sich nicht beschweren. Es ist jedoch falsch, weil Sie versuchen, 10 Zeichen in ein Array von 5 Zeichen zu kopieren (erinnern Sie sich an das Abschlusszeichen), und dies würde außerhalb der Grenzen schreiben, was zu undefined Verhalten führt.

  • In der ersten Erklärung 

    char *str={"what","is","this"}; 
    

    deklariert str einen Zeiger auf eine char und ist ein Skalar. Der Standard sagt das 

    6.7.9 Initialisierung (p11):

    Der Initialisierer für einen Skalar muss ein einzelner Ausdruck sein, der optional in geschweiften Klammern eingeschlossen ist. [...]

    Ein skalarer Typ kann einen eingeschlossenen Initialisierer enthalten, jedoch mit einem einzigen Ausdruck, jedoch im Fall von

    char *str = {"what","is","this"}; // three expressions in brace enclosed initializer
    

    es liegt an den Compilern, wie sie damit umgehen werden. Beachten Sie, dass der Rest der Initialisierer ein Fehler ist. Ein bestätigender Complier sollte eine Diagnosemeldung geben. 

    [Warning] excess elements in scalar initializer   
    

    5.1.1.3 Diagnose (P1): 

    Eine konforme Implementierung muss mindestens eine Diagnosemeldung (in einer implementierungsdefinierten Weise identifiziert) erzeugen, wenn eine Übersetzungseinheit oder eine Übersetzungseinheit vor der Verarbeitung einen Verstoß gegen eine Syntaxregel oder -einschränkung enthält, selbst wenn das Verhalten auch ausdrücklich als undefiniert oder als Implementierung definiert wird. definiert 

  • Sie behaupten, "str[i]="newstring"; ist gültig, wohingegen str[i][j]='j'; ungültig ist.

    str[i] ist vom Typ char und kann nur einen Datentyp char enthalten. Die Zuweisung von "newstring" (was char * ist) ist ungültig. Die Anweisung str[i][j]='j'; ist ungültig, da der Indexoperator nur auf einen Array- oder Zeigerdatentyp angewendet werden kann. 

  • Sie können str[i]="newstring"; funktionieren lassen, indem Sie str als Array von char * deklarieren. 

    char *str[] = {"what","is","this"};
    

    In diesem Fall ist str[i] vom Typ char *, und ihm kann ein String-Literal zugewiesen werden. Wenn Sie jedoch das String-Literal ändern, auf das str[i] verweist, wird ein undefiniertes Verhalten ausgelöst. Das heißt, du kannst str[0][0] = 'W' nicht. 

  • Der Ausschnitt 

    char str[][5]={"what","is","this"};
    

    str als Array von chars-Arrays deklarieren. str[i] ist eigentlich ein Array und da Arrays nicht änderbare Werte sind, können Sie sie nicht als linken Operanden des Zuweisungsoperators verwenden. Dies macht str[i]="newstring"; ungültig. Während str[i][j]='J'; funktioniert, weil Elemente eines Arrays geändert werden können. 

2
haccks

Nur weil Sie sagten, andere Antworten verwirren mich, lassen Sie uns zunächst anhand eines einfacheren Beispiels sehen, was passiert

char *ptr = "somestring";

Hier ist "somestring" ein string-Literal, das in Nur-Lese-Datenabschnitt des Speichers gespeichert ist. ptr ist ein Zeiger (genau wie andere Variablen in demselben Codeabschnitt zugewiesen), der auf das erste Byte dieses zugewiesenen Speichers zeigt.

Beachten Sie daher diese beiden Aussagen

char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a';     //statement 2 error

Anweisung 1 führt eine perfekt gültige Operation aus (Zuweisung eines Zeigers zu einem anderen), aber Anweisung 2 ist keine gültige Operation (versucht, in eine schreibgeschützte Position zu schreiben).

Andererseits, wenn wir schreiben:

char ptr[] = "somestring";

Ptr ist hier eigentlich kein Zeiger, sondern der Name eines Arrays (im Gegensatz zum Zeiger nimmt er keinen zusätzlichen Speicherplatz in Anspruch). Es weist die gleiche Anzahl von Bytes zu, die von "somestring" (nicht schreibgeschützt) benötigt wird, und das ist es.

Betrachten Sie also die gleichen zwei Anweisungen und eine zusätzliche Anweisung

char *ptr2 = ptr; //statement 1 OK
ptr[1] = 'a';     //statement 2 OK
ptr = "someotherstring" //statement 3 error

Anweisung 1 führt eine perfekt gültige Operation aus (Zuweisung eines Array-Namens an einen Zeiger, Array-Name gibt die Adresse des 1. Bytes zurück). Anweisung 2 ist auch gültig, weil der Speicher nicht lesbar ist.

Anweisung 3 ist keine gültige Operation, da ptr hier kein Zeiger ist. Sie kann nicht auf einen anderen Speicherort zeigen.


Jetzt in diesem Code, 

char **str={"what","is","this"};

*str ist ein Zeiger (str[i] ist identisch mit *(str+i))

aber in diesem Code

char str[][] = {"what", "is", "this"};

str[i] ist kein Zeiger. Es ist der Name eines Arrays.

Das gleiche wie oben folgt.

1
Raman

Um die Verwirrung zu beseitigen, müssen Sie ein gutes Verständnis von Zeigern, Arrays und Initialisierern haben .. Ein häufiges Missverständnis unter C-Programmieranfängern ist, dass ein Array einem Zeiger entspricht.

Ein Array ist eine Sammlung von Elementen desselben Typs. Beachten Sie die folgende Erklärung:

char arr[10];

Dieses Array enthält 10 Elemente vom Typ char.

Eine Initialisierungsliste kann verwendet werden, um ein Array auf bequeme Weise zu initialisieren. Das Folgende initialisiert die Array-Elemente mit den entsprechenden Werten der Initialisierungsliste:

char array[10] = {'a','b','c','d','e','f','g','h','i','\0'};

Arrays können nicht zugewiesen werden. Daher ist die Verwendung der Initialisierungsliste nur bei der Array-Deklaration gültig.

char array[10];
array = {'a','b','c','d','e','f','g','h','i','\0'}; // Invalid...

char array1[10];
char array2[10] = {'a','b','c','d','e','f','g','h','i','\0'};
array1 = array2; // Invalid...; You cannot copy array2 to array1 in this manner.

Nach der Deklaration eines Arrays müssen Zuweisungen an Array-Mitglieder über den Array-Indexierungsoperator oder dessen Entsprechung erfolgen.

char array[10];
array[0] = 'a';
array[1] = 'b';
.
.
.
array[9] = 'i';
array[10] = '\0';

Schleifen sind eine übliche und bequeme Methode, um Array-Mitgliedern Werte zuzuweisen:

char array[10];
int index = 0;
for(char val = 'a'; val <= 'i'; val++) {
    array[index] = val;
    index++;
}
array[index] = '\0';

char-Arrays können über String-Literale initialisiert werden, bei denen es sich um konstante nullterminierte char-Arrays handelt:

char array[10] = "abcdefghi";

Folgendes gilt jedoch nicht:

char array[10];
array = "abcdefghi"; // As mentioned before, arrays are not assignable

Kommen wir nun zu den Zeigern ....__ Zeiger sind Variablen, die die Adresse einer anderen Variablen speichern können, normalerweise des gleichen Typs.

Beachten Sie die folgende Erklärung:

char *ptr;

Dies deklariert eine Variable vom Typ char *, einen char-Zeiger. Das heißt, ein Zeiger, der auf eine Variable char zeigen kann.

Im Gegensatz zu Arrays können Zeiger zugewiesen werden. Somit gilt Folgendes:

char var;
char *ptr;
ptr = &var; // Perfectly Valid...

Da ein Zeiger kein Array ist, kann einem Zeiger nur ein einzelner Wert zugewiesen werden.

char var;
char *ptr = &var; // The address of the variable `var` is stored as a value of the pointer `ptr`

Es sei daran erinnert, dass einem Zeiger ein einzelner Wert zugewiesen werden muss. Daher ist Folgendes nicht gültig, da die Anzahl der Initialisierer mehr als eins beträgt:

char *ptr = {'a','b','c','d','\0'};

Dies ist eine Verletzung der Einschränkung, aber Ihr Compiler weist ptr möglicherweise nur 'a' zu und ignoriert den Rest. Aber selbst dann warnt Sie der Compiler, weil Zeichenliterale wie 'a' standardmäßig den Typ int haben und nicht mit dem Typ ptr kompatibel sind, der char * ist.

Wenn dieser Zeiger zur Laufzeit dereferenziert wurde, führt dies zu einem Laufzeitfehler für den Zugriff auf ungültigen Speicher, wodurch das Programm abstürzt.

In deinem Beispiel:

char *str = {"what", "is", "this"};

auch hier handelt es sich um eine Verletzung der Integritätsbedingungen, aber Ihr Compiler kann what die Zeichenfolge str zuweisen, den Rest ignorieren und einfach eine Warnung anzeigen:

warning: excess elements in scalar initializer.

So beseitigen wir die Verwirrung in Bezug auf Zeiger und Arrays: .__ In einigen Zusammenhängen kann ein Array zu einem Zeiger auf das erste Element des Arrays zerfallen. Somit gilt Folgendes:

char arr[10];
char *ptr = arr;

durch die Verwendung des Arraynamens arr in einem Zuweisungsausdruck als rvalue zerfällt das Array in einen Zeiger auf sein erstes Element, wodurch der vorherige Ausdruck äquivalent ist zu:

char *ptr = &arr[0];

Denken Sie daran, dass arr[0] vom Typ char ist und &arr[0] die Adresse vom Typ char * ist, die mit der Variablen ptr kompatibel ist.

Erinnern Sie sich daran, dass String-Literale konstant mit null terminiert sind char arrays ; daher ist der folgende Ausdruck ebenfalls gültig:

char *ptr = "abcdefghi"; // the array "abcdefghi" decays to a pointer to the first element 'a'

In Ihrem Fall ist char str[][5] = {"what","is","this"}; ein Array von 3 Arrays, die jeweils 5 Elemente enthalten.

Da Arrays nicht zuweisbar sind, ist str[i] = "newstring"; nicht gültig, da str[i] ein Array ist, aber str[i][j] = 'j'; ist gültig, da str[i][j] ein Arrayelement ist, dasNICHTein Array von sich selbst ist und zuweisbar ist.

0
machine_1

Fall 1 :

char*str={"what","is","this"};

Vor allem die oben stehende Anweisung ist ungültig, lesen Sie die Warnungen richtig. str ist ein einzelner Zeiger, er kann zu einem Zeitpunkt auf single char array und nicht auf multiple char array zeigen. 

bounty.c: 3: 2: Warnung: zu viele Elemente im Skalar-Initialisierer [standardmäßig aktiviert]}

str ist ein char pointer und wird im section-Abschnitt von RAM gespeichert, aber contents wird im code(Can't modify the content-Abschnitt von RAM gespeichert, da str mit string(in GCC/linux) initialisiert wird. 

wie Sie sagten str [i] = "newstring"; ist gültig, während str [i] [j] = 'j'; ist ungültig.

str= "new string" bewirkt nicht, dass der Abschnitt code/read-only geändert wird. Hier weisen Sie new address einfach str zu. Deshalb ist es gültig

*str='j' oder str[0][0]='j' ist ungültig, da Sie hier den schreibgeschützten Abschnitt ändern und versuchen, den ersten Buchstaben von str zu ändern.

Fall 2: 

char str[][5]={"what","is","this"};

hier str ist 2D array, dh str und str[0],str[1],str[2] selbst sind in stack section von RAM gespeichert, dh Sie können jeden str[i]-Inhalt ändern.

str[i][j]='w'; ist gültig, weil Sie versuchen, den Inhalt der Abschnitte zu stapeln, was möglich ist. aber

str[i]= "new string"; ist nicht möglich, da str[0] selbst ein Array und Array ist const-Zeiger (Adresse kann nicht geändert werden), Sie können keine neue Adresse zuweisen.

Einfach in erster Fallstr="new string" ist valid, weil strpointer ist, keine array und in zweiter Fallstr[0]="new string"not valid, weil strarray keine pointer ist.

Ich hoffe, es hilft.

0
Achal

Fall 1:

Wenn ich schreibe

char*str={"what","is","this"};

dann ist str[i]="newstring"; gültig, während str[i][j]='j'; ungültig ist.

Teil I.I
>> char*str={"what","is","this"};

In dieser Anweisung ist str ein Zeiger auf den Typ char. Beim Kompilieren muss eine Warnmeldung zu dieser Anweisung angezeigt werden:

warning: excess elements in scalar initializer
        char*str={"what","is","this"};
                         ^

Grund für die Warnung ist: Sie stellen einem Skalar mehr als einen Initialisierer zur Verfügung.
[Arithmetische Typen und Zeigertypen werden gemeinsam Skalartypen genannt.]

str ist ein Skalar und von C Standards # 6.7.9p11 :

Der Initialisierer für einen Skalar muss ein einzelner Ausdruck sein, der optional in geschweiften Klammern eingeschlossen ist. ..

Darüber hinaus ist es undefiniertes Verhalten , einem Skalar mehr als einen Initialisierer zuzuweisen.
Von C Standards # J.2 Undefiniertes Verhalten :

Der Initialisierer für einen Skalar ist weder ein einzelner Ausdruck noch ein einzelner Ausdruck in geschweiften Klammern

Da es sich um undefiniertes Verhalten gemäß der Norm handelt, macht es keinen Sinn, darüber weiter zu diskutieren . Besprechen von Teil I.II und Teil I.III mit einer Annahme - char *str="somestring", nur um den char * -Typ besser zu verstehen.
Scheint, dass Sie ein Array von Zeigern für die Zeichenfolge erstellen möchten. Ich habe unten in diesem Beitrag einen kurzen Überblick über das Array von Zeigern für Zeichenfolgen gegeben, nachdem ich über beide Fälle gesprochen habe.

Teil I.II
>> then str[i]="newstring"; is valid

Nein , dies ist nicht gültig .
Auch hier muss der Compiler wegen inkompatibler Konvertierung eine Warnmeldung zu dieser Anweisung anzeigen.
Da str ein Zeiger auf den Typ char ist. Daher ist str[i] ein Zeichen an i Stellen hinter dem Objekt, auf das str [str[i] --> *(str + i)] zeigt.

"newstring" ist ein Zeichenkettenliteral und ein Zeichenkettenliteral zerfällt in einen Zeiger, außer wenn es zum Initialisieren eines Arrays vom Typ char * verwendet wird, und hier versuchen Sie, es einem char zuzuweisen. Art. Daher meldet der Compiler dies als Warnung.

Teil I.III
>> whereas str[i][j]='j'; is invalid.

Ja, das ist ungültig.
Der [] (Indexoperator) kann mit Array- oder Zeigeroperanden verwendet werden.
str[i] ist ein Zeichen und str[i][j] bedeutet, dass Sie [] für den Operanden char verwenden, der ungültig ist. Daher meldet der Compiler dies als Fehler.

Fall 2:

Wenn ich schreibe

char str[][5]={"what","is","this"};

dann ist str[i]="newstring"; nicht gültig, während str[i][j]='J'; gültig ist.

Teil II.I
>> char str[][5]={"what","is","this"};

Das ist absolut richtig. Hier ist str ein 2D-Array. Basierend auf der Anzahl der Initialisierer legt der Compiler automatisch die erste Dimension fest. Die In-Memory-Ansicht von str[][5] würde in diesem Fall ungefähr so ​​aussehen:

         str
         +-+-+-+-+-+
  str[0] |w|h|a|t|0|
         +-+-+-+-+-+
  str[1] |i|s|0|0|0|
         +-+-+-+-+-+
  str[2] |t|h|i|s|0|
         +-+-+-+-+-+

Basierend auf der Initialisierungsliste werden die jeweiligen Elemente des 2D-Arrays initialisiert und die restlichen Elemente werden auf 0 gesetzt.

Teil II.II
>> then str[i]="newstring"; is not valid

Ja, das ist nicht gültig.
str[i] ist ein eindimensionales Array.
Gemäß den C-Standards ist ein Array kein änderbarer Wert.
Aus C Standards # 6.3.2.1p1 :

Ein lWert ist ein Ausdruck (mit einem anderen Objekttyp als void), der möglicherweise ein Objekt kennzeichnet; 64) Wenn ein lWert bei der Auswertung kein Objekt kennzeichnet, ist das Verhalten undefiniert. Wenn ein Objekt einen bestimmten Typ haben soll, wird der Typ durch den Wert angegeben, der zur Bezeichnung des Objekts verwendet wird. Ein modifizierbarer Wert ist ein Wert, der keinen Array-Typ, keinen unvollständigen Typ, keinen konstanten Typ und, wenn es sich um eine Struktur oder eine Vereinigung handelt, kein Mitglied (einschließlich rekursiv jedes Mitglieds) hat oder Element aller enthaltenen Aggregate oder Vereinigungen) mit einem konstanten Typ.

Außerdem wird ein Arrayname in einen Zeiger konvertiert, der auf das Anfangselement des Arrayobjekts zeigt, außer wenn es sich um den Operanden des Operators sizeof, des Operators _Alignof oder des Operators unary & handelt.

Von C Standards # 6.3.2.1p3 :

Außer wenn es sich um den Operanden des Operators sizeof, den Operator _Alignof oder den Operator unary & handelt oder um ein Zeichenfolgenliteral zum Initialisieren eines Arrays handelt, wird ein Ausdruck vom Typ '' Array vom Typ '' in einen Ausdruck mit konvertiert Typ Zeiger auf Typ, der auf das ursprüngliche Element des Array-Objekts zeigt und kein lWert ist.

Da str bereits initialisiert ist und wenn Sie i ein anderes String-Literal zuweisenth Array von str, das String-Literal konvertiert in einen Zeiger, der die Zuweisung inkompatibel macht, da Sie einen Wert vom Typ char Array und einen Wert vom Typ char * haben. Daher meldet der Compiler dies als Fehler.

Teil II.III
>> whereas str[i][j]='J'; is valid.

Ja, dies ist gültig, solange i und j gültige Werte für das angegebene Array str sind.

str[i][j] ist vom Typ char, daher können Sie ihm ein Zeichen zuweisen. Achtung, C überprüft keine Array-Grenzen und der Zugriff auf ein Array außerhalb der Grenzen ist ein undefiniertes Verhalten, das Folgendes beinhaltet: Es kann zufällig genau das tun, was der Programmierer beabsichtigt hat, oder ein Segmentierungsfehler oder das unbemerkt Erzeugen falscher Ergebnisse oder irgendetwas kann passieren.


Angenommen, Sie möchten im Fall 1 ein Array von Zeigern auf eine Zeichenfolge erstellen.
Es sollte so sein:

char *str[]={"what","is","this"};
         ^^

Die In-Memory-Ansicht von str sieht ungefähr so ​​aus:

      str
        +----+    +-+-+-+-+--+
  str[0]|    |--->|w|h|a|t|\0|
        |    |    +-+-+-+-+--+
        +----+    +-+-+--+
  str[1]|    |--->|i|s|\0|
        |    |    +-+-+--+
        +----+    +-+-+-+-+--+
  str[2]|    |--->|t|h|i|s|\0|
        |    |    +-+-+-+-+--+
        +----+

"what", "is" und "this" sind String-Literale.
str[0], str[1] und str[2] sind Zeiger auf das jeweilige Zeichenfolgenliteral, und Sie können sie auch auf eine andere Zeichenfolge verweisen lassen.

Das ist also völlig in Ordnung:

str[i]="newstring"; 

Angenommen, i ist 1, so zeigt der Zeiger str[1] jetzt auf das Zeichenfolgenliteral "newstring":

        +----+    +-+-+-+-+-+-+-+-+-+--+
  str[1]|    |--->|n|e|w|s|t|r|i|n|g|\0|
        |    |    +-+-+-+-+-+-+-+-+-+--+
        +----+

Aber Sie sollten dies nicht tun :

str[i][j]='j';

(unter der Annahme von i=1 und j=0, also ist str[i][j] das erste Zeichen der zweiten Zeichenfolge)

Nach dem Standard führt der Versuch, ein Zeichenfolgenliteral zu ändern, zu undefiniertem Verhalten, da sie möglicherweise schreibgeschützt gespeichert oder mit anderen Zeichenfolgenliteralen kombiniert werden.

Von C Standard # 6.4.5p7 :

Es ist nicht spezifiziert, 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.


Zusätzlich:

In der Sprache C gibt es keinen systemeigenen Zeichenfolgentyp. In der Sprache C ist eine Zeichenfolge eine nullterminierte Matrix Zeichen. Sie sollten den Unterschied zwischen Arrays und Zeigern kennen.

Ich würde vorschlagen, dass Sie zum besseren Verständnis der Arrays, Zeiger und Array-Initialisierung Folgendes lesen:

  1. Aktivieren Sie bei der Array-Initialisierung this .
  2. Überprüfen Sie die Gleichwertigkeit von Zeigern und Arrays mit this und this .
0
H.S.
  • Beginnen mit

    char*str={"what","is","this"};
    

    ist nicht einmal gültiger C-Code 1), so zu diskutieren ist nicht sehr sinnvoll. Aus irgendeinem Grund lässt der gcc-Compiler diesen Code nur mit einer Warnung durch. Ignorieren Sie die Compiler-Warnungen nicht. Wenn Sie gcc verwenden, stellen Sie sicher, dass Sie immer mit -std=c11 -pedantic-errors -Wall -Wextra kompilieren.

  • Was gcc anscheinend bei diesem nicht standardmäßigen Code zu tun scheint, ist es so zu behandeln, als hätten Sie char*str={"what"}; geschrieben. Was wiederum dasselbe ist wie char*str="what";. Dies wird durch die C-Sprache keinesfalls garantiert.

  • str[i][j] versucht, einen Zeiger zweimal zu indirektisieren, obwohl er nur eine Ebene der Indirektion hat und daher ein Compiler-Fehler angezeigt wird. Es macht so wenig Sinn wie das Tippen 

    int array [3] = {1,2,3}; int x = array[0][0];.

  • Zum Unterschied zwischen char* str = ... und char str[] = ... siehe FAQ: Was ist der Unterschied zwischen Zeichen [] und Zeichen *? .

  • Im Fall char str[][5]={"what","is","this"}; wird ein Array von Arrays (2D-Array) erstellt. Die innerste Dimension wird auf 5 gesetzt, und die äußerste Dimension wird automatisch vom Compiler festgelegt, abhängig davon, wie viele Initialisierer der Programmierer bereitstellt. In diesem Fall 3 entspricht der Code char[3][5].

  • str[i] gibt Ihnen die Arraynummer i im Array von Arrays. Sie können Arrays in C nicht zuordnen, da die Sprache so gestaltet ist. Außerdem wäre es ohnehin falsch, dies für einen String zu tun. FAQ: Wie kann ich einen neuen Stringwert richtig zuweisen?


1) Dies ist ein Verstoß gegen C11 6.7.9/2. Siehe auch 6.7.9/11.

0
Lundin