Warum ist der Parameter endptr für strtof und strtod ein Zeiger auf einen nicht konstanten char-Zeiger?

Lesezeit: 6 Minuten

Benutzer-Avatar
Daniel Trebien

Die Standardfunktionen der C-Bibliothek strtof und strtod habe folgende Unterschriften:

float strtof(const char *str, char **endptr);
double strtod(const char *str, char **endptr); 

Sie zerlegen jeweils die Eingabezeichenfolge, strin drei Teile:

  1. Eine anfängliche, möglicherweise leere Folge von Leerzeichen
  2. Eine “Subjektsequenz” von Zeichen, die einen Gleitkommawert darstellen
  3. Eine “Nachfolge” von Zeichen, die nicht erkannt werden (und die Konvertierung nicht beeinflussen).

Wenn endptr ist nicht NULLdann *endptr wird auf einen Zeiger auf das Zeichen gesetzt, das unmittelbar auf das letzte Zeichen folgt, das Teil der Konvertierung war (mit anderen Worten, der Beginn der abschließenden Sequenz).

Ich frage mich: warum ist endptrdann ein Zeiger auf a nicht-const char Zeiger? Ist nicht *endptr ein Zeiger auf a const char Zeichenfolge (die Eingabezeichenfolge str)?

  • Es ist im Grunde das gleiche Problem wie strchr und Freunde, außer dass wir hier eher einen Out-Param-Zeiger als einen Rückgabewert haben.

    – Steve Jessop

    6. Oktober 2010 um 15:45 Uhr

  • @Steve: ja, aber es ist problematischer als strchr weil du a nicht bestehen kannst const-qualifizierte Zeigeradresse auf diese Funktionen ohne explizite Umwandlung.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. Oktober 2010 um 15:48 Uhr

  • Interessante Frage. Im Grunde bedeutet dies, dass Sie eine Besetzung ausblenden können char const* zu char* hinter strtoX Funktionen. Seltsam.

    – Jens Gustedt

    6. Oktober 2010 um 15:50 Uhr

  • @Jens: Es ist ärgerlich, aber in C ist es unvermeidlich, da es keine Funktionsüberladung gibt. In C++ gibt es zwei strchr Funktionen, Rückkehr char* wenn die Eingabe ist char*und const char* wenn die Eingabe ist const char*. Es gibt keine solche Überladung von strtod im C++-Standard, und ich kenne die Begründung dafür nicht. Und bevor jemand fragt, es gibt kein strtof überhaupt in C++, da es nicht in C89 ist. Eine Suche zeigt, dass mir diese Begründung lange Zeit nicht bekannt war: stackoverflow.com/questions/993700/are-strtol-strtod-unsafe

    – Steve Jessop

    6. Oktober 2010 um 16:08 Uhr


Benutzer-Avatar
R.. GitHub HÖREN SIE AUF, ICE ZU HELFEN

Der Grund ist einfach die Benutzerfreundlichkeit. char * automatisch umwandeln kann const char *aber char ** kann nicht automatisch konvertiert werden const char **und der tatsächliche Typ des Zeigers (dessen Adresse übergeben wird), der von der aufrufenden Funktion verwendet wird, ist viel wahrscheinlicher char * als const char *. Der Grund, warum diese automatische Konvertierung nicht möglich ist, ist, dass es eine nicht offensichtliche Möglichkeit gibt, damit die zu entfernen const Qualifizierung durch mehrere Schritte, wobei jeder Schritt für sich vollkommen gültig und korrekt aussieht. Steve Jessop hat in den Kommentaren ein Beispiel gegeben:

wenn Sie automatisch konvertieren könnten char** zu const char**dann könntest du das machen

char *p;
char **pp = &p;
const char** cp = pp;
*cp = (const char*) "hello";
*p = 'j';.

Aus Gründen der Const-Sicherheit muss eine dieser Zeilen illegal sein, und da die anderen alle vollkommen normale Vorgänge sind, muss sie es auch sein cp = pp;

Ein viel besserer Ansatz wäre gewesen, diese Funktionen zu definieren, um sie zu übernehmen void * anstelle von char **. Beide char ** und const char ** automatisch umwandeln kann void *. (Der durchgestrichene Text war eigentlich eine sehr schlechte Idee; er verhindert nicht nur jegliche Typprüfung, sondern C verbietet tatsächlich Objekte vom Typ char * und const char * zu Alias.) Alternativ hätten diese Funktionen auch folgendes annehmen können: a ptrdiff_t * oder size_t * Argument, in dem die gespeichert werden soll versetzt des Endes, eher als ein Hinweis darauf. Das ist ohnehin oft sinnvoller.

Wenn Ihnen der letztere Ansatz gefällt, können Sie gerne einen solchen Wrapper um die Standardbibliotheksfunktionen schreiben und Ihren Wrapper aufrufen, um den Rest Ihres Codes zu behalten const-sauber und gussfrei.

  • “Beide char** und const char** kann automatisch in void * konvertiert werden.” Ich verstehe, was Sie sagen und verwenden void* würde es dem Benutzer ermöglichen, a zu übergeben const char** ohne Besetzung. Aber es würde ihnen auch erlauben, ohne Besetzung in einem ganzen Universum falscher Dinge zu passieren. Angesichts der Einschränkungen von C denke ich, dass es besser ist, die grundlegende Typsicherheit beizubehalten, sogar die Kosten für den Verlust der Konstantensicherheit.

    – Steve Jessop

    6. Oktober 2010 um 15:48 Uhr


  • Es ist nicht nur die Benutzerfreundlichkeit, diese Funktionen speichern das Ergebnis in diesem Zeiger, und Sie können nicht in einen konstanten Speicher schreiben.

    Benutzer405725

    6. Oktober 2010 um 15:49 Uhr

  • “Falls es jemand hat, bitte posten.” – wenn Sie automatisch konvertieren könnten char** zu const char**dann könntest du das machen char *p; char **pp = &p; const char** cp = pp; *cp = (const char*) "hello"; *p = 'j';. Aus Gründen der Const-Sicherheit muss eine dieser Zeilen illegal sein, und da die anderen alle vollkommen normale Vorgänge sind, muss sie es auch sein cp = pp;.

    – Steve Jessop

    6. Oktober 2010 um 15:56 Uhr

  • Ein Offset löst sicherlich das const-Problem, obwohl es jetzt zu spät ist, diese Konvention konsequent in allen Bibliotheken zu verwenden, und es zu dem Zeitpunkt möglicherweise bereits zu störend war const erfunden wurde, bin ich mir nicht sicher. Wenn es normal wäre, dass String-Handling-Funktionen statt Zeigern Offsets zurückgeben, würde strcpy immer 0 zurückgeben? 😉

    – Steve Jessop

    6. Oktober 2010 um 18:49 Uhr

  • Im Idealfall strcpy würde die Länge der Zeichenfolge zurückgeben, eine sehr nützliche Information, die sie als Nebeneffekt ihrer Operation automatisch erhält (und dann wegwirft) .. 🙂

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    6. Oktober 2010 um 19:11 Uhr

Benutzer-Avatar
Aidan Cully

Benutzerfreundlichkeit. Das str Argument ist als gekennzeichnet const da das Eingabeargument nicht geändert wird. Wenn endptr war constdann würde das die anweisen Anrufer dass er Daten, auf die verwiesen wird, nicht ändern sollte endptr bei der Ausgabe, aber oft möchte der Aufrufer genau das tun. Zum Beispiel möchte ich eine Zeichenfolge mit Null beenden, nachdem ich den Float daraus entfernt habe:

float StrToFAndTerminate(char *Text) {
    float Num;

    Num = strtof(Text, &Text);
    *Text="\0";
    return Num;
}

Unter bestimmten Umständen eine absolut vernünftige Sache, die man tun möchte. Funktioniert nicht wenn endptr ist vom Typ const char **.

Im Idealfall, endptr sollte eine Konstante sein, die der tatsächlichen Eingabekonstanz von entspricht str, aber C bietet keine Möglichkeit, dies durch seine Syntax anzuzeigen. (Anders Hejlsberg spricht darüber bei der Beschreibung warum const wurde in C# weggelassen.)

  • Der gleiche Effekt könnte leicht mit erreicht werden Text[FloatEnd-Text] = '\0' Also für mich ist das keine gute Ausrede. Die Methode, die Sie angeben, erzeugt UB if Text würde mit deklariert werden const und Sie könnten das nicht einfach von dem Ort aus lesen, an dem Sie die Aufgabe erledigen.

    – Jens Gustedt

    6. Oktober 2010 um 16:14 Uhr

  • @Jens: Ich habe den Code aktualisiert, um die Begründung besser widerzuspiegeln. Und ich stimme dem undefinierten Verhaltensproblem zu. Die Frage ist, ob die Heilung schlimmer ist als die Krankheit…

    – Aidan Cully

    6. Oktober 2010 um 16:46 Uhr


  • “könnte leicht erreicht werden” – aber was für uns eine vorzeitige Optimierung ist (wir bevorzugen den Zeiger direkt, anstatt uns auf den Compiler zu verlassen, um diesen Ausdruck zu sortieren, und / oder die Hardware so schnell zu sein, dass es uns egal ist), war nicht t vorzeitige Optimierung an das C89-Normenkomitee, wenn sie es spezifizierten strtod. Außerdem ist das eine beschämende Redewendung, die man lernen muss, also fragen wir uns nur, ob es mehr oder weniger beschämend ist als const-Incorrenceness 😉

    – Steve Jessop

    6. Oktober 2010 um 16:46 Uhr


  • @Steve: hm, verfrühte Optimierung, rechnerisch ist das gerade so Text+FloatEnd-Textdas ist nicht wirklich schwer von einem Compiler zu optimieren, nein ?-)

    – Jens Gustedt

    6. Oktober 2010 um 18:09 Uhr

  • @Aidan: Ich hoffe, du hast es nicht beleidigt, ich glaube, dass die Motivation so gewesen sein könnte, wie du vorschlägst, aber ich würde dir die Schuld geben 😉

    – Jens Gustedt

    6. Oktober 2010 um 18:10 Uhr

1333540cookie-checkWarum ist der Parameter endptr für strtof und strtod ein Zeiger auf einen nicht konstanten char-Zeiger?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy