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?
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?
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 Typsconst 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 char
wä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
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
const char**
const char**
(wie du es gerade tust)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.
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