Frage zum L-Wert (lvalue). Warum hat eine Char-Array keinen, aber ein C-Sting einen L-wert?



  • Siehe Frage.

    #include <iostream>
    
    using namespace std;
    
    int main (){
    
    	const char  ar[] = "Hallo";        
    	const char ar2[] = "hallo2";    
    
    	const char * str = "tschuess";
    	const char * str2 = "tschuess2";
    
    	str = ar;                      // das geht
    	cout << str << endl;
    
    	ar2 = str2;                    // das nicht
    	cout << ar2 << endl; 
    
    }
    

    Ps: Sollte man eigentlich einen Array immer mit const deklarieren? Was für genaue folgen hat das?



  • Weil Dennis Ritchie und Brian Kernighan es so definiert haben. Eine bessere Antwort wird es wohl nicht geben.

    const char* ist ein Zeiger auf ein char. Dahinter kann sich ein C-String verbergen, muss aber nicht!

    Nein, sollte man nicht, nur dann, wenn man den Inhalt nicht ändern können will.

    Benutze std::string! Wirklich!



  • win8789 schrieb:

    Siehe Frage.

    #include <iostream>
    
    using namespace std;
    
    int main (){
    	
    	const char  ar[] = "Hallo";        
    	const char ar2[] = "hallo2";    
    	
    	const char * str = "tschuess";
    	const char * str2 = "tschuess2";
    	
    	str = ar;                      // das geht
    	cout << str << endl;
    	
    	ar2 = str2;                    // das nicht
    	cout << ar2 << endl; 
    	
    }
    

    Ps: Sollte man eigentlich einen Array immer mit const deklarieren? Was für genaue folgen hat das?

    Regel Nr. 1. Es werden keine Fragen im Threadtitel gestellt.

    Einem Array kannst du nichts zuweisen.

    const char  ar[] = "Hallo";
    

    Ist nur die Kurzform für

    const char  ar[] = {'H','a','l','l','o','\0' };
    

    Wenn du dein Array später ändern willst, machst du kein const, ansonsten schon.

    str und str2 sind Zeiger. Zeiger kann man etwas zuweisen, solange sie nicht const sind. Deine Zeiger sind nicht const. Der Inhalt, auf den dein Zeiger zeigt, ist const.

    Es gibt.

    char* p;
    const char* pp;
    char* const p;
    const char* const ppp;
    

    In Anführungszeichen eingeschlossene Zeichen sind immer vom Typ const char[].



  • Wieso definiere ich etwas als const wenn ich es später ändern möchte?
    Macht doch nicht wirklich Sinn.



  • EOP schrieb:

    Wieso definiere ich etwas als const wenn ich es später ändern möchte?
    Macht doch nicht wirklich Sinn.

    Naja in diesem Beispiel funktioniert es. Ich habe es ursprünglich ohne const gesprieben, allerdings hat dann mein Compiler eine Wahrung ausgegeben. Geändert hat das einfügen von const allerdings nichts.(außer das der Compiler nun still ist)



  • win8789 schrieb:

    EOP schrieb:

    Wieso definiere ich etwas als const wenn ich es später ändern möchte?
    Macht doch nicht wirklich Sinn.

    Naja in diesem Beispiel funktioniert es. Ich habe es ursprünglich ohne const gesprieben, allerdings hat dann mein Compiler eine Wahrung ausgegeben. Geändert hat das einfügen von const allerdings nichts.(außer das der Compiler nun still ist)

    Von welchem const reden wir? Was hat den Compiler schweigen lassen?



  • out schrieb:

    Von welchem const reden wir? Was hat den Compiler schweigen lassen?

    //Keine Wahrnung
    	const char  ar[] = "Hallo";
    	const char ar2[] = "hallo2";
    
    	const char * str = "tschuess";
        const char * str2 = "tschuess2";
    
    	str = ar;
    	cout << str << endl;
    
    //Wahrung ([Warning] deprecated conversion from string constant to 'char*' //[-Wwrite-strings]
    
    	char  ar[] = "Hallo";
    	char ar2[] = "hallo2";
    
    	 char * str = "tschuess";
         char * str2 = "tschuess2";
    
    	str = ar;
    	cout << str << endl;
    


  • win8789 schrieb:

    out schrieb:

    Von welchem const reden wir? Was hat den Compiler schweigen lassen?

    char  ar[] = "Hallo";
    	char ar2[] = "hallo2";
    	
    	const char * str = "tschuess";
        const char * str2 = "tschuess2";
    	
    	str = ar;
    	cout << str << endl;
    

    Auch keine Wahrnung.



  • gelöscht.



  • Ok nachedem ich mir jetzt nochmal ein paar Gedanken über das Thema gemacht habe, den Komentar von Out ein paar mal gelesen habe

    out schrieb:

    str und str2 sind Zeiger. Zeiger kann man etwas zuweisen, solange sie nicht const sind. Deine Zeiger sind nicht const. Der Inhalt, auf den dein Zeiger zeigt, ist const.

    ich nochmal ein paar Stellen, aus einem Buch zum Thema, gelesen habe, bin ich zur folgendert Erklärung gekommen(bitte korrigiert sie, falls sie falsch und/oder unkomplet ist):

    Da eine C-sting eine Zeiger ist hat er einen L-Wert. Da aber ein Char-Array ein Array ist und Arrays, trotz der sehr nahen Verwandschaft zur Zeiger, unterschiede haben, hat er keinen L-Wert. Der Grund dafür ist, dass ein Arrays im eigentlichen Sinne keinen Speicherplatzt haben, da bei ihr, genau wie bei einer Konstante, keine Speicherzelle zugeordnet sein muss, weil der Compiler den Wert jedesmal direkt verwenden kann, ist keine Speicherzelle notwendig, die die Adresse der Arrays enthält. Es wird wie ein konstanter Zeiger behandelt, der ebenfalls keinen L-wert hat.

    Danke nochmal an Out.



  • Mit deinen Aussagen bin ich nicht einverstanden. Wie heißt das Buch?

    Eine Zeichenkette "Ich bin eine Zeichenkette" hat den Typ const char[] , also ein Array aus const chars. Bis auf wenige Ausnahmen (z.B. sizeof(array)) zerfällt ein Array bei seiner Nutzung in einen Zeiger auf das erste Element des Arrays, in diesem Fall also const char* . Darum geht const char * str = "tschuess"; Und das bezeichne ich auch nicht als Verwandschaft sondern als ein Array das sich implizit in einen Zeiger auf das erste Element konvertieren lässt.

    Doch Arrays haben natrülich einen Speicherplatz, siehe sizeof. Du kannst auch einen Zeiger auf ein Array machen, z.B. onst char(*p)[9] = &"tschuess";

    Du solltest dir darüber als Anfänger nicht den Kopf zerbrechen. Du musst dir jetzt nur merken dass
    - Ein Zeiger ist ein Zeiger.
    - Ein Array ist ein Array.
    - Ein Array kann implizit in einen Zeiger auf das erste Element konvertiert werden.



  • out schrieb:

    Mit deinen Aussagen bin ich nicht einverstanden. Wie heißt das Buch?

    Eine Zeichenkette "Ich bin eine Zeichenkette" hat den Typ const char[] , also ein Array aus const chars. Bis auf wenige Ausnahmen (z.B. sizeof(array)) zerfällt ein Array bei seiner Nutzung in einen Zeiger auf das erste Element des Arrays, in diesem Fall also const char* . Darum geht const char * str = "tschuess"; Und das bezeichne ich auch nicht als Verwandschaft sondern als ein Array das sich implizit in einen Zeiger auf das erste Element konvertieren lässt.

    Doch Arrays haben natrülich einen Speicherplatz, siehe sizeof. Du kannst auch einen Zeiger auf ein Array machen, z.B. onst char(*p)[9] = &"tschuess";

    Du solltest dir darüber als Anfänger nicht den Kopf zerbrechen. Du musst dir jetzt nur merken dass
    - Ein Zeiger ist ein Zeiger.
    - Ein Array ist ein Array.
    - Ein Array kann implizit in einen Zeiger auf das erste Element konvertiert werden.

    Das Buch ist "C++ Einführung und professionale Programmierung" von Ulrich Breymann 6.Aktualiesierte Ausgabe. Hanser 2001.

    Ich Zitiere hier mal einen Absatz aus den Kapitel 6.2 (Seite 201):
    " Ein Array besitzt in diesem Sinne keinen Speicherplatz. Genau wie einer Konstanten keine Speicherzelle zugeordnet sein muss, weil der Compiler den Wert jedesmal direkt verwenden kann, ist keine Speicherzelle notwendig, die die Adresse des Arrays enthält. Ein Array ist vielmehr ein symbolischer Name für die Adresse(= den Anfang) eines Bereiches im Speicher. Der Name wird syntaktisch wie ein konstanter Zeiger behandelt."-Zitat ende.

    Das nächste Kamptel (6.2.1) heißt: C-Arrays und sizeof.
    Es beginnt mit:"C-Arrays sind als >>roher Speicher<< (englisch raw memory)keine Objekte in dem bisherigen Sinn. Es gibt keine Methode, die verwenden werden könnte:

    for (int i = 0; i<Kosten.size(); ++i)  //nicht bei C-Arrays!
    cout << i << ": " << Kosten[i] << endl;
    

    Die Größe eines C-Arrays kann nicht vom ihm erfragt werden, sie muss vielmehr vorher bekannt oder mit sizeod ermittelt worden sein."- Zitat ende.



  • Hmm also das lese ich so jetzt zum ersten mal. Und da ich nun kein C++ Profi bin überlasse ich die Klarstellung der Richtigkeit mal den schlauen Köpfen hier.



  • Es soll wohl heißen, dass ein Compiler zwar einen Platz im Memory für eine nicht-static-Konstante anlegen *kann*, aber nicht *muss*, wenn sie nur in derselben Übersetzungseinheit verwendet wird. Deswegen müssen konstante, die in einer anderen Übersetzungseinheit verwendet werden, als "extern" deklariert werden. Dann haben sie definitiv eine Adresse.

    ANDERERSEITS: Da eine Konstante mit Namen einen L-wert darstellt, auch wenn der nicht veränderbar ist, müsste sie doch eine Adresse haben??? Wer weiß das?

    Zum Zeigerverhalten:

    char g[10];
    *g = 'a'; // ok, g ist wie ein Zeiger auf nicht-const Bereich
    ++g;   // geht nicht! (der Zeiger selbst kann nicht verändert werden)
    


  • sebi709 schrieb:

    ANDERERSEITS: Da eine Konstante mit Namen einen L-wert darstellt, auch wenn der nicht veränderbar ist, müsste sie doch eine Adresse haben??? Wer weiß das?

    Ein lvalue muss erst dann eine Adresse haben, wenn man auf diese auch tatsächlich zugreift. Und selbst dann - wenn der Compiler feststellt, dass er die gesamte Berechnung,
    die dein Programm ausführen soll auch zur Compile-Zeit erledigen kann (Stichw. Konstantenfaltung et al.), dann wird wahrscheinlich das einzige, bei der Ausführung eine tatsächliche,
    physische Adresse hat, das Endergebnis sein, das an den "cout-Aufruf" übergeben wird - selbst wenn der Code mit lvalues nur so gespickt ist :D.
    Man kann sich eine Adresse von einem lvalues holen und damit arbeiten als läge der Wert irgendwo im Speicher.
    Ob sich das Objekt auch tatsächlich irgendwo im Speicher befindet, liegt im Ermessen des Compilers - solange du den Unterschied nicht bemerkst, und sich das Programm verhält "als wenn" dem so wäre.
    Das wird wohl auch einer der Günde sein, weshalb man so etwas wie "extern" benötigt.

    sebi709 schrieb:

    char g[10];
    *g = 'a'; // ok, g ist wie ein Zeiger auf nicht-const Bereich
    ++g;   // geht nicht! (der Zeiger selbst kann nicht verändert werden)
    

    Letzteres geht übrigens nicht, da Arrays keine Pointer sind - es existiert hier lediglich eine implizite Konvertierung in einen char* -rvalue.
    ++g funktioniert also aus demselben Grund nicht, weshalb ++42 nicht geht. 😉

    Finnegan



  • Hallo,
    Ich habe mich gestern Abend nochmal hingesetzt und habe versucht eine Ausführliche Antwort auf meine Frage zu schreiben. Hierfür habe ich nochmal einige Stellen aus meinem Buch gründlich gelesen. Falls meine Erklärung faslch ist oder Fehler hat korrigiert diese bitte. Erklärung:

    Ein C-Array hat keinen L-Wert auf grund seiner Speicherstruktur. Hierfür nehme ich als bespiel einfachmal eine int array. Den Speicherplatz kann man sich ungefähr so verstellen:

    int Tabelle[4] = {9,8,7,6};
    
        Speicheradresse(nur sysmbolich);  Wert  ;Platz im Array
                 1                         9       [0] <- "Tabelle" ist ein Zeiger
                 5                         8       [1]    auf den Anfang des Arrays
                 9                         7       [2]
                13                         6       [3]
    

    Der Compiler reserviert vier (genau genommen 16, weil int) Speicherzellen, die FEST sind und NEBENEINANDER liegen. Der Name des Arrays hat keinen L-Wert, das er fest und unveränderlich auf das Erste Objekt des Arrays zeigen muss. Das erste objekt besitzt hierbei die Adresse des Arrays-Anfang. Deshalb, weil der Speicherplatz fest und nicht verändert werden kann, da sonst die Informationen über die Adresse (der Anfang) des Zeigers verloren gehen würde, kann der Compiler ein Array (Hier ist expiziet der Name des Arrays an sich gemeint) genau wie eine konstante behandeln und muss keine Speicherzelle reservieren, welhce die Adresse beinhaltet(die Adresse weis der Compiler sowieso schon durch (Tabelle[0] (der Array-Anfang)). Deshalb kann der Compiler den Wert dirket verwenden, da er ja nicht änderbar ist. Ein Array ist vielmehr der Symbolische Name für die Adresse (=Anfang).Der Name wird syntaktische wie ein Konstanter Zeiger behanderlt.

    Was wäre wenn:
    Angenommen man könnte nun aber folgendes machen:

    char *str = "Hallo";   // der Anfang von str ist z.B die Adrsse Nr. 50
    Tabelle = str         // mir ist bewusst, dass Tabelle von Typ int ist und 
                          //diese so auch nicht klappen könnte wenn Tabelle einen //L-Wert hätte. Aber nur mal als Beispiel.
    

    Wenn man nun aber wie hier im beispiel die Adresse des Arrays verändern würde, sodass er auf str zeigt, wird aber gleichzeitig NICHT die Adresse von z.B Tablle[1] verändert werden. Diese wird nun immer noch vier Speicherzellen nach den "Origanelle" "Tabelle" stehen (Die "Originalle" Tabelle zeigt auf 1 ). Wenn nun aber der Compiler nun das Objekt Zerstören will, würde er noch *(Tabelle +1) suchen, welches ja nun nicht mehr auf das gesuchte Objekt (welches sich eigentlich in der Speicherzelle 5 befindet) zeigt.



  • win8789 schrieb:

    Hallo,

    Was wäre wenn:
    Angenommen man könnte nun aber folgendes machen:

    char *str = "Hallo";   // der Anfang von str ist z.B die Adrsse Nr. 50
    Tabelle = str         // mir ist bewusst, dass Tabelle von Typ int ist und 
                          //diese so auch nicht klappen könnte wenn Tabelle einen //L-Wert hätte. Aber nur mal als Beispiel.
    

    Wenn man nun aber wie hier im beispiel die Adresse des Arrays verändern würde, sodass er auf str zeigt, wird aber gleichzeitig NICHT die Adresse von z.B Tablle[1] verändert werden. Diese wird nun immer noch vier Speicherzellen nach den "Origanelle" "Tabelle" stehen (Die "Originalle" Tabelle zeigt auf 1 ). Wenn nun aber der Compiler nun das Objekt Zerstören will, würde er noch *(Tabelle +1) suchen, welches ja nun nicht mehr auf das gesuchte Objekt (welches sich eigentlich in der Speicherzelle 5 befindet) zeigt.

    const char*str ... wie schon gesagt. Aber Tabelle = str; geht nicht, und mir wenigstens ist unklar, was Du eigentlich sagen willst.



  • sebi710 schrieb:

    const char*str ... wie schon gesagt. Aber Tabelle = str; geht nicht, und mir wenigstens ist unklar, was Du eigentlich sagen willst.

    Ich will eigentlich nur Zeigen was Passieren würde, wenn mein anfängliches Beispiel gehen würde. Eine was wäre wenn Sitation erschaffen und erklären, was das für Fehler mitsichführen könnte(siehe dazu die Erklärung untendrunter).

    P.s const char*str würde die selben fehler verurschen, nähmlich das, dass Objekt z.b Tabelle[1] (das Original Obajekt mit den Wert 😎 nicht mehr auffindbar wäre.


Log in to reply