const char *var oder string ???



  • Hi.
    beim Durchgehen einiger .cpp oder .h Dateien auf bspw. Github sehe ich immer wieder das sehr oft ein

    const char *var
    

    als Funktionsparameter erwartet wird.
    Wieso verwendet man nicht

    std::string &var
    

    oder ähnliches?
    Was ist der Unterschied zwischen einem string und einem char-pointer?

    Ich stecke noch nicht all zu tief in der Materie drin, daher die Frage.
    Danke euch.



  • std::stringist eine Klasse die zu C++ gehört. Diese kennt C nicht.

    Mit char * oder char[] realisiert man sog. C-Strings. Damit kann C++ auch umgehen.

    Es gibt noch reichlich C-Libraries die auch unter C++ genutzt werden - oder Programmierer die das falsch gelernt haben.



  • Wenn man die Funktionen von std::string in der aufgerufenen Funktion nicht braucht ist es einfacher aus einem std::string einen Pointer auf char const zu machen als aus einem Pointer auf char const einen std::string:

    void hugo(char const *str);
    // ...
    std::string str = "foobar";
    hugo(str.c_str());
    

    vs.

    void hugo(std::string const &str);
    // ...
    char const *str = "foobar";
    hugo(std::string{ str });
    


  • Ah ok. Also macht man das, damit die Libraries sowohl von C als auch C++ genutzt werden kann?

    Eine dumme Frage muss ich direkt noch hinterherwerfen, sorry dafür:
    wieso kann ich in

    char *var
    

    einen ganzen Satz schreiben, wenn char eigentlich nur einzelne Zeichen speichern kann?



  • Ich muss noch mal kurz ergänzen.
    Also ich verstehe, was char array ist, ich frage mich nur, wieso ich, bevor ich

    const char *var
    

    benutze kein char array definieren muss

    char text[100] = "Irgendwas"
    

    Wieso funktioniert der Pointer ohne die Definition?



  • @robert123 sagte in const char *var oder string ???:

    wieso kann ich in

    char *var
    

    einen ganzen Satz schreiben, wenn char eigentlich nur einzelne Zeichen speichern kann?

    Kannst Du nicht. Du kannst in var die Adresse zu einem char speichern. var zeigt auf ein Zeichen das auch der Beginn eines C-Strings (sz-String, String-Zero-Terminated, Nullterminierte Zeichenkette) sein kann.



  • @robert123 sagte in const char *var oder string ???:

    Also ich verstehe, was char array ist, ich frage mich nur, wieso ich, bevor ich

    const char *var;
    

    benutze kein char array definieren muss

    char text[100] = "Irgendwas";
    

    Wieso funktioniert der Pointer ohne die Definition?

    So wie das da steht ist da ein uninitialisierter Pointer var der irgendwohin Zeigt. Den zu dereferenzieren (das ansprechen auf das er Zeigt) gibt undefiniertes Verhalten.

    Wenn aber der Zeiger auf text Zeigt:

    var = text;    // oder var = &text[0];
    

    ist alles gut.



  • Ah ok.
    Also nur zum Verständis:

    Ich habe irgendwo Text, sei es in einem string, einem c-string oder char array.
    dem char pointer übergebe ich die adresse, und der zeigt auf den Anfang dessen.
    Ist das so richtig?

    der char pointer kann also mit jeder oben genannten Variablen bestückt werden,
    da ja alle irgendwie mit char zu tun haben.
    Kann man das so stehen lassen?



  • @robert123 sagte in const char *var oder string ???:

    Ich habe irgendwo Text, sei es in einem string, einem c-string oder char array.
    dem char pointer übergebe ich die adresse, und der zeigt auf den Anfang dessen.
    Ist das so richtig?

    char ch = 'A';
    char *foo = "foo";
    char bar[] = "bar";
    std::string qux = "qux";
    
    char *p = &ch;  // p Zeigt auf ch
    p = foo; // oder auch p = foo; ... p zeigt auf das 'f' auf das auch foo zeigt.
    p = bar; // oder auch p = &bar[0]; ... p zeigt auf das 'b' das im ersten Element des Arrays bar steht.
    p = &qux[0]; // p zeigt auf das 'q' des strings "qux" der sonstwo im Speicher steht.
    
    char const *pstr = qux.c_str();  // c_str() gibt einen Pointer auf char const zurück.
    // pstr zeigt auf das 'q' das irgendwo im Speicher steht.


  • Ich möchte als Kandidat noch std::string_view in den Raum werfen.



  • Vielleicht noch wichtig: du kannst dem char* nicht ansehen, ob das ein Zeiger auf ein einzelnes Zeichen oder einen C-String ist. Technisch ist es ja immer ein Zeiger auf ein Zeichen. Du musst es also selbst wissen, ob du den char* als Zeiger auf einen C-String ansehen willst. Die cout-Ausgabe weiß das auch nicht, d.h. std::cout nimmt immer an, dass ein char-Pointer auf einen nullterminierten String zeigt.

    Falsch:

    #include <iostream>
    int main() {
      char c = 'X';
      char *pc = &c; 
      std::cout << pc; // FALSCH!
    }
    

    Der Zeiger muss auch nicht auf das erste Zeichen eines C-Strings zeigen:

    #include <iostream>
    int main() {
      char arr[] = "0123456789\n";
      char *pc = &arr[5]; 
      std::cout << pc; // ok, aber nicht Anfang
    }
    


  • Ich danke euch sehr für die Ausführungen aber jetzt bin ich verwirrt.
    @Swordfish

    In deinem code steht zum einen:

    char *foo = "foo";
    

    und zum anderen

    char *p = &ch;
    

    Eine Zeigervariable speichert doch nur Adressen, wie kann es sein, dass man ihn mit einer zeichenkette Initialisieren kann?
    Wenn ich das richtig verstanden habe, dann zeigt dieser Zeiger auf das "f" von "foo" richtig?
    aber wieso kann ich ihn mit einer Zeichenkette initialisieren? Alles was ich bisher über Zeiger gelesen habe, hatte immer etwas mit dem Adressoperator zu tun.

    @wob
    Ich habe gerade den oberen code von dir ausprobiert und es kommt nicht das gewünschte Ergebnis raus,
    also keine Adresse. Angezeigt wird mir zwar das "X", aber gefolgt von irgendwelchen Zeichen.
    Ok, das scheint wie du sagst Falsch zu sein, aber wie kann ich denn über einen char pointer die Adresse erfragen?
    Mit einem integer pointer und einer int Variable funktioniert es ja eben genau so.

    Danke euch nochmal vielmals.
    Ihr habt sicher besseres zu tun, als solche Basics zu besprechen.
    🙏🏻



  • @robert123 sagte in const char *var oder string ???:

    Eine Zeigervariable speichert doch nur Adressen, wie kann es sein, dass man ihn mit einer zeichenkette Initialisieren kann?

    Weil der Typ eines String literal Array of N const char ist:

    https://en.cppreference.com/w/cpp/language/string_literal:

    1. Narrow multibyte string literal. The type of an unprefixed string literal is const char[N], where N is the size of the string in code units of the execution narrow encoding, including the null terminator.

    Standart: 5.13.5 String literals [lex.string]



  • Ok, und ein array selbst auf das erste Element von sich selbst zeigt, richtig.
    War das richtig ausgedrückt?



  • @robert123 sagte in const char *var oder string ???:

    wie kann ich denn über einen char pointer die Adresse erfragen?

    Es gibt eine Überladung des operator<<() für ostreams und char const*1). Diese Überladung gibt nicht die Adresse aus sondern interpretiert den Pointer als den Beginn einer nullterminierten Zeichenkette. Um die Adresse auszugeben kannst Du nach void* casten:

    #include <iostream>
    
    int main()
    {
        char const *foo = "foo";
        std::cout << '"' << foo << "\"\n" << "Address: " << static_cast<void const*>(foo) << '\n';
    }
    
    1. https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2 :
    1. Behaves as an FormattedOutputFunction. After constructing and checking the sentry object, inserts successive characters from the character array whose first element is pointed to by s.
      For the first and third overloads (where CharT matches the type of ch), exactly traits::length(s) characters are inserted.
      For the second overload, exactly std::char_traits<char>::length(s) characters are inserted.
      For the last two overloads, exactly traits::length(reinterpret_cast<const char*>(s)) are inserted.
      Before insertion, first, all characters are widened using os.widen(), then padding is determined as follows: if the number of characters to insert is less than os.width(), then enough copies of os.fill() are added to the character sequence to make its length equal os.width(). If (out.flags()&std::ios_base::adjustfield) == std::ios_base::left, the fill characters are added at the end of the output sequence, otherwise they are added before the output sequence. After insertion, width(0) is called to cancel the effects of std::setw, if any.
      The behavior is undefined if s is a null pointer.


  • Oh man.
    Das habe ich so noch nie gehört "...nach void* casten. 🙃
    Es liegt noch einiges an Arbeit vor mir.
    Danke dir



  • @robert123 sagte in const char *var oder string ???:

    Ich habe gerade den oberen code von dir ausprobiert und es kommt nicht das gewünschte Ergebnis raus,
    also keine Adresse.

    Habe ich ja versucht zu beschreiben. Man möchte mit cout C-Strings ausgeben können. Daher wird char* von cout (bzw. dem operator<<) spezialbehandelt, nämlich so, als würde es auf einen auszugebenden C-String zeigen - und vom cout ist eben nicht unterscheidbar, ob das stattdessen ein normaler Pointer auf ein genau ein char (und keinen String) sein soll. Der Code gibt also das X und dann das aus, was danach im Speicher steht. Dorthin darf aber nicht zugegriffen werden. Daher ist das Verhalten des Programms undefiniert. Wenn du mit dem Address Sanitizer kompilierst (Parameter -fsanitize=address bei g++/clang), siehst du, dass das Programm damit einen stack-buffer-overflow erzeugt.



  • Danke dir.
    Das muss ich mir merken. Das habe ich bisher in keinem Buch gefunden.


Log in to reply