Aufruf strcat C++



  • Hey Leute,

    ich möchte in meinen C++-Programm einfach nur einen C-String an einen bereits vorhandenen C-String anhängen.
    Dazu gibt es ja die Standardfunktion char* strcat (char* s1, const char* s2) , welche 2 Zeiger als Funktionsargumente
    erwartet.
    Wenn ich nun einen C-String char str1[50] = "Alexa" initialisiere und diese Funktion wie folgt aufrufe:
    strcat(&sr1, "Jones") , erhalte ich immer eine Fehlermeldung. Warum...eigentlich muss der Adressoperator beim Aufruf von Funktionen, welche als Argument einen Zeiger auf ein bestimmtes Objekt erwarten, doch immer mit angegeben werden? Warum kann ich bei konstanten Zeiger als Funktionsargument den String (in dem Fall "Jones") gleich direkt mit im Funktionsaufruf angeben? Vielen Dank für eure Hilfe!


  • Mod

    str1 ist doch schon ein Zeiger…

    Wenn du nicht weißt, warum das so ist, wäre es dringend zu empfehlen, nicht mit C-Strings rumzuhacken, sondern std::string von C++ zu benutzen. Das verhält sich wie man es von "normalen" Variablen (und Strings in anderen Programmiersprachen) gewöhnt ist, ohne miese Sonderfallstricke.



  • Also erstmal warum es nicht so geht wie du es geschrieben hast: &str1 ist ein "Zeiger auf char[50]". Die Funktion braucht aber einen "Zeiger auf char". Und C++ erlaubt da einfach keine implizite Konvertierung.

    Warum es ohne & geht: C++ erlaubt die implizite Konvertierung von "Referenz auf T-Array" zu "Zeiger auf T".

    Wenn du es mit & schreiben willst, dann kannst du &str1[0] statt &str1 schreiben. Das geht auch. Damit holst du dir explizit einen Zeiger auf das erste Element von str1.



  • @SeppJ Nö, ist kein Zeiger, ist ein Array. Zerfällt nur ggf. automatisch in einen Zeiger.


  • Mod

    @hustbaer sagte in Aufruf strcat C++:

    @SeppJ Nö, ist kein Zeiger, ist ein Array. Zerfällt nur ggf. automatisch in einen Zeiger.

    Kommt halt auf den Kontext an. In den meisten Fällen ist der Ausdruck str1 ein Zeiger, doch. Wenn ich mich recht erinnere ist die Formulierung im Standard sogar von der Art "Es ist ein Zeiger, außer in diesen Ausnahmefällen" und dann eine Liste der Ausnahmen.

    PS: Aus C99, in C++ wird es sicher ähnlich stehen:

    Except when it is the operand of the sizeof operator or the unary & operator, or is a
    string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
    converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
    the array object and is not an lvalue. If the array object has register storage class, the
    behavior is undefined.

    Ist jetzt eine feine Interpretation. Da steht ja quasi, wenn es Array Type hat, dann hat es keinen Array Type (außer in den Ausnahmen). Hat es dann Array Type oder nicht? Ich würde sagen, Nein, hat es nicht.



  • @SeppJ

    C++ ist aber nicht C.

    #include <iostream>
    
    template <class T>
    void revelio_type(T const& t) {
        std::cout << typeid(t).name() << std::endl;
    }
    
    int main() {
        char arr[123];
        revelio_type(arr);
        revelio_type(&arr);
        revelio_type(&arr[0]);
    }
    

    =>

    A123_c
    PA123_c
    Pc
    

    Mal ganz davon abgesehen dass ich es komisch finde str1 als Ausdruck zu betrachten. str1 ist die Variable, und die Variable ist ganz klar kein Zeiger.



  • @SeppJ sagte in Aufruf strcat C++:

    ..."Es ist ein Zeiger, außer in diesen Ausnahmefällen" und dann eine Liste der Ausnahmen.

    PS: Aus C99, in C++ wird es sicher ähnlich stehen:

    Except when it is the operand of the sizeof operator or the unary & operator, or is a
    string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
    converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
    the array object and is not an lvalue. If the array object has register storage class, the
    behavior is undefined.

    Ist jetzt eine feine Interpretation. Da steht ja quasi, wenn es Array Type hat, dann hat es keinen Array Type (außer in den Ausnahmen). Hat es dann Array Type oder nicht? Ich würde sagen, Nein, hat es nicht.

    Ich tu mich schwer da ein "ist ein Zeiger" herauszulesen. Ich lese da eher "ist ein Array", das eben bei Bedarf konvertiert wird, was ja durchaus "in dem meisten Fällen" so sein darf.

    Ich vermute mal in C++ gibts da wohl noch deutlich mehr "Ausnahmen". So ein Array-Typ kann ja durchaus an einige Dinge binden, ohne dabei seine Identität zu verlieren. Abgeleitete Template-Typen, auto, eine Referenz char (&)[50] oder gar einen Zeiger char (*)[50].



  • Und:
    @SeppJ sagte in Aufruf strcat C++:

    In den meisten Fällen ist der Ausdruck str1 ein Zeiger, doch.

    Nö. In den meisten Fällen wird str1 in einen Zeiger konvertiert. Zu sagen str1 ist in den meisten Fällen ein Zeiger, wäre gleich unsinnig wie zu sagen ein String-Literal wäre meistens ein Zeiger. Oder wie zu sagen dass char Variablen oft ints wären.



  • Mal ganz davon abgesehen dass ich es komisch finde str1 als Ausdruck zu betrachten. str1 ist die Variable [...]

    Das hingegen finde ich absolut nicht komisch, sondern völlig naheliegend. Ist nicht jeder Variablenname auch ein Ausdrück, wenn er nicht gerade in einer Deklaration steht?


  • Mod

    @hustbaer sagte in Aufruf strcat C++:

    Und:
    @SeppJ sagte in Aufruf strcat C++:

    In den meisten Fällen ist der Ausdruck str1 ein Zeiger, doch.

    Nö. In den meisten Fällen wird str1 in einen Zeiger konvertiert. Zu sagen str1 ist in den meisten Fällen ein Zeiger, wäre gleich unsinnig wie zu sagen ein String-Literal wäre meistens ein Zeiger. Oder wie zu sagen dass char Variablen oft ints wären.

    Wenn sie aus Sicht der Sprache genau das sind, wieso ist dann deine Interpretation die Wahrheit? Da steht die Zeichenkette str1 im Quelltext. Dann, wenn der Quelltext übersetzt wird, ist das ein Zeiger. Dazwischen steht eine Interpretationskette in der Sprachdefinition. Diese Kette ist aber abstrakt und nicht beobachtbar. Es kann sogar gut sein, dass die Zwischenzustände in den Datenstrukturen des Compilers überhaupt nicht existieren. Wieso ist solch ein willkürlich gewählter Zwischenschritt die Wahrheit, und nicht das Endergebnis dieser Kette?

    PS: Du hast aber Recht in dem Punkt, dass es im C++-Standard andersherum steht. Da steht sinngemäß, dass dieser Ausdruck genau dann zu einem Pointer konvertiert wird, wenn die Arrayinterpretation in dem Kontext keinen Sinn ergibt.



  • @SeppJ sagte in Aufruf strcat C++:

    Wieso ist solch ein willkürlich gewählter Zwischenschritt die Wahrheit, und nicht das Endergebnis dieser Kette?

    Hmmm.. ein double ist ein char, ausser wenn er nicht an einen char bindet? Mag ja sein, dass er eigene Typ in dem Fall nur auf dem Papier existiert, aber "wird konvertiert" statt "ist" hat Konsequenzen, die m.E. für den Anwender zum besseren Verständnis betragen. Z.B. dass Array-Typen auch noch eine Größe haben, die man nicht immer zwangsläufig wegwerfen muss. Manchmal ist es nützlich, die mitzuführen 😉


  • Mod

    @Finnegan sagte in Aufruf strcat C++:

    Hmmm.. ein double ist ein char, ausser wenn er nicht an einen char bindet?

    Da hast du irgendwie Recht. Aber Gegenstandpunkt: Im Array-Fall ist es ja schon eher die Ausnahme, dass das Array tatsächlich wie ein Array zählt. C++ mag es zwar andersherum formulieren als C, aber trotzdem gibt es effektiv nur 4-6 Fälle (je nach Version des Sprachstandards), wo das Array wirklich wie ein Array zählt. Und alle davon sind so komische Meta-Fälle, wo direkt auf den Typen operiert wird, anstatt die "normale" Nutzung einer Variablen als rvalue.



  • @SeppJ sagte in Aufruf strcat C++:

    @Finnegan sagte in Aufruf strcat C++:

    Hmmm.. ein double ist ein char, ausser wenn er nicht an einen char bindet?

    Da hast du irgendwie Recht, aber in diesem Fall ist es ja schon eher die Ausnahme, dass das Array tatsächlich wie ein Array zählt. C++ mag es zwar andersherum formulieren als C, aber trotzdem gibt es effektiv nur 4-6 Fälle (je nach Version des Sprachstandards), wo das Array wirklich wie ein Array zählt.

    Ja, ich mach auch viel zu wenig in C. Da gibts glaube ich echt nicht viel außer vielleicht sizeof, was man mit der Array-Eigenschaft anfangen kann. Da würd's mir wohl auch so gehen. In C++ erhalte ich hin und wieder mal den Array-Typen, z.B. via Template-Parameter. Da kann man sich in einigen Fällen (meist Lowlevel-Zeug) schonmal einen extra size-Funktionsparameter sparen, wenn die Größe eh im Typen eincodiert ist.



  • @SeppJ sagte in Aufruf strcat C++:

    Wieso ist solch ein willkürlich gewählter Zwischenschritt die Wahrheit, und nicht das Endergebnis dieser Kette?

    Weil man so über C++ reden kann. Wenn man nur das Endergebnis betrachtet, dann kann man nicht mehr sinnvoll erklären wie man C++ programmiert. Dann muss man zu jeder Referenz Zeiger sagen. Ausser sie wird vielleicht ganz wegoptimiert. Wie nennt man es dann? Ding was wir hinschreiben aber im Programm nicht mehr vorkommt?

    Ne. Es macht schon Sinn zu einem Array Array zu sagen, und nicht Zeiger.

    ps: Und wenn man sich willkürlich andere Zwischenschritte rauspickt, dann wird es auch schwer. Im Kontext kann das schon Sinn machen. Aber so allgemein... weniger. Und einem Anfänger zu sagen "ist doch schon ein Zeiger" ist kein Kontext wo ich es für sinnvoll halte.


  • Mod

    Fair.
    Aber die Antwort auf die Frage des OP ist immer noch, dass das für den Funktionsaufruf ein Pointer ist, auch wenn da nur str1 (ohne &) steht. Weil die Sprache sagt, dass das so ist, wenn man den "Wert" eines Arrays nimmt.



  • Ich finde die Forumlierung einfach nicht sinnvoll. str1 ist ein Array, egal wo es steht. Die Antwort für den OP ist, dass es in C++ "array to pointer decay" gibt. Also dass Arrays ggf. implizit in einen Zeiger auf ihr erstes Element konvertiert werden. Und dass er deswegen str1 schreiben kann, trotz dem dass str1 ein Array ist und die Funktion einen Zeiger erwartet.



  • @hustbaer Die Funktion bekommt eine Adresse, die sie in einem Zeiger speichert.
    Ob die jetzt von einem Array stammt oder aus einem anderen Zeiger ist da dann nicht mehr feststellbar.



  • @DirkB sagte in Aufruf strcat C++:

    @hustbaer Die Funktion bekommt eine Adresse, die sie in einem Zeiger speichert.
    Ob die jetzt von einem Array stammt oder aus einem anderen Zeiger ist da dann nicht mehr feststellbar.

    s1 ist ein char*, keine Frage. str1 ist jedoch ein char[50]. Wenn man eine Funktion wie strcat schreibt, dann gehört dazu auch die Parameterliste - und dort könnte man beide sehr wohl noch unterscheiden, wenn man denn wollte.



  • @DirkB str1 ist nicht der Parameter sondern das Argument.
    Dass die Parameter von strcat Zeiger sind wurde nie bestritten.



  • Alles klar...vielen Dank für eure Antworten


Log in to reply