Warum erzeugt das Übergeben von char** als const char** eine Warnung?

Lesezeit: 7 Minuten

Benutzeravatar von green
grün

Ich habe diese Warnung erhalten:

note: expected ‘const char **’ but argument is of type ‘char **’

Im Moment übergebe ich die Argumente, indem ich sie umwandle const char **. Gibt es eine andere Möglichkeit, wie ich es loswerden kann?

  • Könnten Sie den von Ihnen verwendeten Funktionsprototyp bereitstellen?

    – Eregrith

    28. Januar 2013 um 13:15 Uhr

  • Sicher. int CompareRecords(const char **, const char **, const int *, const int)

    – grün

    28. Januar 2013 um 13:28 Uhr

Benutzeravatar von WhozCraig
WhozCraig

Kurze Antwort

Können Sie sicher typisieren char ** zu const char**? Nein. (Jedenfalls nicht sicher), und der Grund ist viel subtiler, als Sie vielleicht denken. Kann man es anders loswerden? Sicher. Laden Sie ein Array von const char* Werte von Ihnen char* Werte und übergeben Sie diese stattdessen. (oder ändern Sie den aufgerufenen Prototyp, aber das ist Betrug = P).

Betrachten Sie den folgenden Code, der im Wesentlichen alles tut, was Sie wünschen außer eine Funktion aufrufen. Die markierte Linie zeigt den äquivalenten Wurfpunkt

const char *s = "Test";
char *p = NULL;
char **pp = &p;             // Put address of our pointer in our pointer-to-pointer.
const char **cpp = pp;      // Here: assigning  char** to const char**
*cpp = s;                   // perfectly legal; pp and s both finish "char const"
*p = 0;                     // ru ro raggy

Es dauert eine Weile, um das wirklich anzustarren, und zugegebenermaßen habe ich es zuerst auch nicht gesehen. @sheu hat es ungefähr 24 Stunden lang gut verstanden, bevor ich wirklich lange genug darüber nachgedacht habe, um zu erkennen, dass er die ganze Zeit Recht hatte (und ich habe diese Antwort tatsächlich positiv bewertet, bevor ich diese geschrieben habe). Dann dachte ich, er hätte sich geirrt, ungefähr zur gleichen Zeit, als er dachte, seine Antwort sei nicht anwendbar. Es stellte sich heraus, dass wir es waren beide bei diesem Sprung falsch, weil er beim ersten Mal Recht hatte, ich lag beim zweiten Mal falsch, und jetzt … pfui.

Auf VS2012 und VS2010 zeigt die markierte Zeile einen Fehler ohne Umwandlung an. klirren wird es mit einer Warnung in C kompilieren, es aber zulassen (was ich überraschend fand). Zugegeben, Sie müssen wirklich aus Ihrem glücklichen Ort heraustreten, um es zu brechen, aber es ist trotzdem kaputt.

Der Rest davon ist eine Schmährede über die Identifizierung von Zeigertypen, ihre Konstanz und was äquivalent zu was ist.


Lange Schmährede über Zeiger und Const

Die Warnung ist, weil char ** und const char ** sind nicht gleichwertig (duh). Um richtig zu sein, könnten Sie den Prototyp (Callée) reparieren oder den Aufrufer reparieren (indem Sie ein Array von const char * und das passieren). Aber können Sie das Erste sicher in das Zweite umwandeln? Hmmm….

Denken Sie daran, nach dem Standard const geht der Artikel sofort zu seiner links. Es ganz links in einem Datentyp zu deklarieren, ist eine nette Eigenschaft, die die Sprache unterstützt, führt aber oft zu Verwirrung oder Problemen. Als Faustregel gilt, wenn const ganz links in einem Decl direkt vor dem Typ erscheint, bezieht es sich auf die Daten Typ; nicht der nachfolgende Zeiger (falls vorhanden). Wenn es rechts erscheint von irgendetwas es gilt für den decl-Teil unmittelbar links, sei es ein Datentypteil oder ein Zeigerteil, aber egal was, es gilt nur für a Single Teil.

Es folgt eine Fülle von Beispielen:

Keine Umleitung:

const char ch;    // const character. must be initialized.
char const ch;    // same as above

Single-Indirektion:

char *p;               // p is mutable, *p is mutable
const char *p;         // p is mutable, *p is const
char const *p;         // same as above.
char *const p;         // p is const, *p is mutable, must be initialized.
char const *const p;   // p is const, *p is const, must be initialized.

Doppelte Indirektion:

char **p;        // ptr-to-ptr-to-char
                 // p, *p, and **p are ALL mutable

const char **p;  // ptr-to-ptr-to-const-char
                 // p and *p are mutable, **p is const

char const **p;  // same as above

char *const *p;  // ptr-to-const-ptr-to-char
                 // p is mutable, *p is const, **p is mutable.

char **const p;  // const-ptr-to-ptr-to-char
                 // p is const, *p is mutable, **p is mutable.
                 // must be initialized.

const char **const p;  // const-ptr-to-ptr-to-const-char
                       // p is const, *p is mutable, **p is const.
                       // must be initialized.

char const **const p;  // same as above

char const *const *p;  // ptr-to-const-ptr-to-const-char
                       // p is mutable, *p is const, **p is const.

const char *const *p;  // same as above.

char *const *const p;  // const-ptr-to-const-ptr-to-char
                       // p is const, *p is const, **p is mutable.
                       // must be initialized.

Und natürlich, wer kann das Haus verlassen, ohne…

char const *const *const p;   // const-ptr-to-const-ptr-to-const-char
                              // everything is const.
                              // must be initialized.

const char *const *const p;   // same as above

Wie wirkt sich das auf Ihre Frage aus? Wenn Sie diesen Code in C ohne Umwandlung kompilieren, erhalten Sie eine Compiler-Warnung (oder einen Fehler beim Kompilieren mit -Werror). Beim Kompilieren in C++ erhalten Sie einfach einen Fehler, weil die Parametersignatur nicht übereinstimmt. Aber wieso?

Da diese keine direkte Äquivalenz haben:

const char **p;  // ptr-to-ptr-to-const-char
                 // p and *p are mutable **p is const

char **p;        // ptr-to-ptr-to-char
                 // p, *p, and **p are all mutable

Beim Kompilieren mit klirrenwird die genaue Warnung in C wie folgt angegeben:

main.c:15:9: Bestanden char ** zum Parameter des Typs const char ** verwirft Qualifizierer in verschachtelten Zeigertypen.

VS2010 und VS2012 hingegen geben beide einen Fehler aus:

Fehler C2440: „Initialisieren“: Konvertieren von „char **“ in „const char **“ nicht möglich

Es scheint seltsam, aber VS ist eigentlich richtiger (Wunder hören nie auf).

Und das macht absolut Sinn. Eingebettet in die Typdeklaration ist die Tatsache, dass die erste davon keine Änderung der endgültigen Daten zulässt, die zweite tut. Von oben wissen wir das char ** und const char ** (aka. char const **), sind nicht das Gleiche. Unten in einem befindet sich ein Zeiger auf a const charwährend der andere einen Zeiger auf hat char.

  • Beachten Sie, dass das Casting an char const * const * IST sicher (da Sie nicht ändern können, worauf es zeigt). (Ich habe dies mit dem Typsystem von OCaml verifiziert.) Unglücklicherweise scheinen die GCC-Leute den Big-Hammer-Ansatz verwendet zu haben, um ALLE solchen Umwandlungen zu verbieten, und Clang scheint sie kopiert zu haben.

    – Chris Pacejo

    5. April 2013 um 18:18 Uhr

  • @ChrisK Danke, Chris. Nachdem ich das, was ich jetzt vor über drei Monaten geschrieben habe, noch einmal gelesen habe, kann ich nicht glauben, dass ich das ausgelassen habe (und doch habe ich das letzte Beispiel eingefügt, das sich dadurch unterscheidet, dass der Basiszeiger konstant ist, duh). Ich werde versuchen, die Antwort zu aktualisieren, um diese Ergänzung widerzuspiegeln. Danke noch einmal. Edit: Ich habe es nicht ausgelassen. Es ist da, aber Ihr Punkt auf der Sicherheit scheint genau zu sein.

    – WhozCraig

    5. April 2013 um 21:22 Uhr


sheus Benutzeravatar
schau

bearbeiten: Ich habe sogar die falsche Frage beantwortet. Meine Antwort ist völlig irrelevant! Ignoriert mich bitte.

bearbeiten 2: Nachdem der Gentleman-Fragesteller seine Frage geklärt hat, stellt sich heraus, dass meine Antwort tatsächlich relevant ist. So ist das Leben.

Dies ist ein lustiges Stück C, was Sinn macht, wenn Sie genau genug darüber nachdenken.

Grundsätzlich die Umrechnung:

char** ptr;
const char** const_ptr;
const_ptr = ptr;  // <-- BAD!

ist nicht erlaubt.

Warum, könnten Sie fragen? “Ich mache Sachen mehr konstant! Das ist natürlich gut so!”


Nun, denken Sie darüber nach. Wenn das erlaubt wäre, dann:

const char c="A";
char* ptr;
const char** const_ptr = &ptr;  // <-- ILLEGAL, but what if this were legal?
*const_ptr = &c;
*ptr="B";  // <- you just assigned to "const char c" above.

BAM, du bist tot. Also… nein 🙂

  • Sie können Ihre Antwort löschen, wenn sie nicht relevant ist.

    – Nachtturno

    28. Januar 2013 um 13:26 Uhr

  • +1 (vor langer Zeit, aber erwähnenswert). Dies war die Antwort, die meine Arbeit letztendlich zum Laufen gebracht hat, und wenn der Gelegenheitsleser dies noch nicht getan hat, verdient sie sicherlich eine Abstimmung.

    – WhozCraig

    12. September 2013 um 4:02 Uhr

Die Warnung sagt Ihnen, dass die aufgerufene Funktion den angegebenen Parameter als erwartet const char** aber du übergibst a char** Parameter. Um diese Warnung loszuwerden, könnten Sie

  • wirklich in a passieren const char**
  • wandeln Sie Ihren Parameter in a um const char** (wie du es gerade tust)
  • Ändern Sie den Funktionsprototypen so, dass die Funktion a erwartet char**

  • Als dritte Option würde ich besser die Funktion Prototyp ändern const char * const *wenn die Funktion die Zeichen nicht ändern muss.

    – Alex Che

    13. August 2015 um 6:56 Uhr

Ich finde das immer noch nicht richtig. Im Beispiel:

const char c="A";
char* ptr;
const char** const_ptr = &ptr;  // <-- ILLEGAL, but what if this were legal?
*const_ptr = &c;
*ptr="B";  // <- you just assigned to "const char c" above.

Die Zeile, die dies unterbricht, lautet:

*const_ptr = &c;

Weil wir einen nicht konstanten Unterzeiger auf einen konstanten Zeiger setzen. Wenn dies wo:

const char *const *const_ptr

Dann

*const_ptr = &c 

wäre richtig und es würde Ihnen nicht erlauben, dem const char c zuzuweisen.

Ich denke nicht, dass der Compiler Sie davon abhalten sollte, etwas Konstanteres zu machen.

1393970cookie-checkWarum erzeugt das Übergeben von char** als const char** eine Warnung?

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

Privacy policy