2008-09-16 14 views
55

Der folgende Codeausschnitt (richtig) eine Warnung in C und einen Fehler in C++ gibt (mit gcc & g ++ jeweils mit den Versionen 3.4.5 und 4.2.1 getestet; MSVC zu kümmern scheint nicht):Warum kann ich in C nicht 'char **' in 'const char * const *' umwandeln?

char **a; 
const char** b = a; 

Ich kann das verstehen und akzeptieren.
Die C++ - Lösung für dieses Problem besteht darin, b zu ändern, um ein const char * const * zu sein, das die Neuzuweisung der Zeiger nicht zulässt und Sie daran hindert, const-correctity zu umgehen (C++ FAQ).

char **a; 
const char* const* b = a; 

jedoch in reinem C, die korrigierte Version (const char * const * verwendet) gibt immer noch eine Warnung, und ich verstehe nicht, warum. Gibt es eine Möglichkeit, dies zu umgehen, ohne einen Cast zu verwenden?

Zur Verdeutlichung:
1) Warum generiert dies eine Warnung in C? Es sollte vollständig const-safe sein, und der C++ - Compiler scheint es als solches zu erkennen.
2) Was ist der richtige Weg, dieses Char ** als Parameter zu akzeptieren, während ich sage (und den Compiler durchsetzen), dass ich die Zeichen, auf die es zeigt, nicht verändern werde? Zum Beispiel, wenn ich eine Funktion schreiben wollte:

void f(const char* const* in) { 
    // Only reads the data from in, does not write to it 
} 

Und ich wollte sie auf ein Zeichen aufzurufen **, was wäre der richtige Typ für den Parameter sein?

Bearbeiten: Vielen Dank an diejenigen, die geantwortet haben, vor allem diejenigen, die die Frage adressiert und/oder meine Antworten verfolgt.

Ich habe die Antwort akzeptiert, dass was ich tun möchte, kann nicht ohne eine Besetzung getan werden, unabhängig davon, ob es möglich sein sollte oder nicht.

+0

Ich bin verwirrt auf Ihre Frage, ist es: "Wie bekomme ich C, mich nicht zu warnen?" oder ist es "Wie bekomme ich C++, um einen Kompilierungsfehler nicht zu werfen?" Oder vielleicht ist es keiner von diesen. – user7116

+0

Ich stimme sechs Variablen zu, diese Frage bedarf weiterer Klärung. –

+0

Die Frage ist: "Warum kann ich 'char **' nicht in 'char * const * konvertieren?" – Kevin

Antwort

53

Ich hatte dieses gleiche Problem vor ein paar Jahren und es ärgerte mich zu keinem Ende.

Die Regeln in C sind einfacher ausgedrückt (d. H. Sie enthalten keine Ausnahmen wie Konvertieren char** zu const char*const*). Consequenlty, es ist einfach nicht erlaubt. Mit dem C++ - Standard enthielten sie mehr Regeln, um solche Fälle zuzulassen.

Am Ende ist es nur ein Problem in der C-Norm. Ich hoffe, dass der nächste Standard (oder technischer Bericht) darauf eingehen wird.

+0

Warum abgelehnt? Bitte erkläre! – Kevin

+0

Ich habe es sicherlich nicht abgelehnt; es ist die relevanteste Antwort, die ich bisher bekommen habe. Wenn ich 100 Rufpunkte hätte, würde ich das wählen. – HappyDude

+1

@Kevin: Wenn Sie immer noch da sind, frage ich mich, wie sicher Sie Ihre Antwort sind, dass es nur ein Problem im Standard ist - zu der Zeit haben Sie tatsächlich den Standard durchlaufen, um danach zu suchen? Wenn ja, kann ich diese Frage so gut wie schließen, denn das ist genauso eine Antwort wie ich bekommen werde. – HappyDude

11

> jedoch in reinem C, das gibt noch eine Warnung, und ich verstehe nicht, warum bereits

Sie das Problem identifiziert hat - dieser Code ist nicht const-korrekt. "Const Korrekt" bedeutet, dass außer const_cast und C-artigen Umwandlungen, die const entfernen, niemals ein const-Objekt durch diese const-Zeiger oder Referenzen verändert werden kann.

Der Wert von const-correctness - const ist zum großen Teil auf Programmiererfehler zurückzuführen. Wenn Sie etwas als const deklarieren, sagen Sie, dass Sie nicht glauben, dass es geändert werden sollte - oder zumindest sollten diejenigen, die nur Zugriff auf die const-Version haben, diese nicht ändern können. Bedenken Sie:

void foo(const int*); 

Wie erklärt, foo nicht Erlaubnis hat die ganze Zahl, auf die von ihr Argument zu ändern.

Wenn Sie nicht sicher sind, warum der Code, den Sie geschrieben nicht const-korrekt ist, sollten Sie den folgenden Code, nur geringfügig von HappyDude Code:

char *y; 

char **a = &y; // a points to y 
const char **b = a; // now b also points to y 

// const protection has been violated, because: 

const char x = 42; // x must never be modified 
*b = &x; // the type of *b is const char *, so set it 
     //  with &x which is const char* .. 
     //  .. so y is set to &x... oops; 
*y = 43; // y == &x... so attempting to modify const 
     //  variable. oops! undefined behavior! 
cout << x << endl; 

Nicht konstante Typen können nur zu konst konvertieren beschreibt insbesondere Möglichkeiten, um eine Umgehung von "const" auf einem Datentyp ohne explizite Umwandlung zu verhindern.

Objekte, die ursprünglich als const deklariert wurden, sind besonders speziell - der Compiler kann annehmen, dass sie sich nie ändern. Wenn jedoch 'b' der Wert von 'a' ohne eine Umwandlung zugewiesen werden kann, könnten Sie versehentlich versuchen, eine const-Variable zu ändern. Dies würde nicht nur die Prüfung unterbrechen, die Sie vom Compiler gestellt bekommen haben, um Sie daran zu hindern, diesen Variablenwert zu ändern - es würde Ihnen auch erlauben, die Compiler-Optimierungen zu brechen!

Bei einigen Compilern wird dies "42" drucken, bei einigen "43" und anderen wird das Programm abstürzen.

Bearbeiten-add:

HappyDude: Dein Kommentar ist zu erkennen. Entweder die C-Sprache oder der C-Compiler, den Sie verwenden, behandelt const char * const * grundlegend anders als die C++ - Sprache es behandelt. Vielleicht sollten Sie die Compiler-Warnung nur für diese Quellzeile deaktivieren.

Bearbeiten-löschen: entfernt Typo

+4

Was Sie gesagt haben, ist richtig, aber es befasst sich nicht mit der Frage. Wie gesagt, ich verstehe, warum die Konvertierung von einem char ** in ein const char ** falsch ist. Was ich nicht verstehe ist, warum die Konvertierung von einem char ** zu einem const char * const * falsch ist. Ich habe meine Frage zur Klärung aktualisiert. – HappyDude

+3

Es ist interessant, dass diese Antwort immer wieder abgestimmt wird. Ich denke, viele Leute lesen nicht über die ersten paar Zeilen der Frage hinaus, etwas, an das ich mich für die Zukunft erinnern werde. Obwohl dies eine ziemlich gut geschriebene Post an einem relativ obskuren Punkt ist, wünschte ich mir nur, dass es relevant war: P. – HappyDude

+3

@Aaron: Ihre Post ist nicht besonders relevant für das Thema, aber trotzdem gut geschrieben. – user7116

0

ich nicht in der Lage bin, einen Fehler zu erhalten, wenn implizit char Casting ** char const * const *, zumindest auf MSVC 14 (VS2k5) und g ++ 3.3. 3. GCC 3.3.3 gibt eine Warnung aus, von der ich nicht genau weiß, ob sie korrekt ist.

test.c:

#include <stdlib.h> 
#include <stdio.h> 
void foo(const char * const * bar) 
{ 
    printf("bar %s null\n", bar ? "is not" : "is"); 
} 

int main(int argc, char **argv) 
{ 
    char **x = NULL; 
    const char* const*y = x; 
    foo(x); 
    foo(y); 
    return 0; 
} 

Ausgang mit der Kompilierung als C-Code: cl/TC/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Ausgang mit der Kompilierung als C++ Code: cl/TP/W4/Wp64 test.c

test.c(8) : warning C4100: 'argv' : unreferenced formal parameter 
test.c(8) : warning C4100: 'argc' : unreferenced formal parameter 

Ausgang mit gcc: gcc -Wall test.c

012.351.
test2.c: In function `main': 
test2.c:11: warning: initialization from incompatible pointer type 
test2.c:12: warning: passing arg 1 of `foo' from incompatible pointer type 

Ausgabe mit g ++: g ++ -Wall test.c

keine Ausgabe

+0

Entschuldigung, ich hätte klarstellen sollen, dass ich mit gcc kompiliere. Ich habe meine Frage aktualisiert, um dies zu klären. Zum Glück (oder vielleicht nicht?) Ich selten auf viele schwierig zu behandelnden Warnungen in msvc. – HappyDude

+0

@HappyDude: Ich habe mit meinen Erkenntnissen aus GCC aktualisiert. Und jetzt sehe ich, was Sie sagen, aber ich stimme der Compilerwarnung noch nicht zu. Ich werde meine C-Spezifikation ausarbeiten und sehen, was ich finde. – user7116

+0

@sixlettervariables: Danke für die Antwort! Lassen Sie es mich wissen, wenn Sie etwas finden, aber ich vermute, dass das, was Kevin gesagt hat, richtig war und es einfach nicht vom Standard abgedeckt wird. – HappyDude

0

Ich bin ziemlich sicher, dass das Schlüsselwort const kann nicht die Daten implizieren nicht konstant geändert/werden soll, nur dass die Daten als schreibgeschützt behandelt werden. Stellen Sie sich Folgendes vor:

const volatile int *const serial_port = SERIAL_PORT; 

das ist gültiger Code. Wie können volatile und const koexistieren? Einfach. volatile weist den Compiler an, den Speicher immer zu lesen, wenn die Daten verwendet werden, und const weist den Compiler an, einen Fehler zu erzeugen, wenn versucht wird, mit dem Zeiger serial_port in den Speicher zu schreiben.

Unterstützt const den Optimierer des Compilers? Nein überhaupt nicht. Da Konstanz den Daten durch Casting hinzugefügt und daraus entfernt werden kann, kann der Compiler nicht herausfinden, ob const Daten wirklich konstant sind (da die Umwandlung in einer anderen Übersetzungseinheit durchgeführt werden könnte).In C++ haben Sie auch das veränderbare Schlüsselwort, um die Angelegenheit weiter zu komplizieren.

char *const p = (char *) 0xb000; 
//error: p = (char *) 0xc000; 
char **q = (char **)&p; 
*q = (char *)0xc000; // p is now 0xc000 

Was passiert, wenn ein Versuch, den Speicher zu schreiben, die wirklich nur gelesen wird, gemacht wird (ROM, zum Beispiel) definiert ist wahrscheinlich nicht in der Norm.

+2

Dies geht nicht wirklich auf die Frage ein. Wie ich es verstehe, ist das Entfernen von Const über Casting technisch eine undefinierte Operation nach dem C-Standard, obwohl die meisten Compiler es zulassen und "das Richtige tun". Aber es ist nicht das, worum ich überhaupt frage. – HappyDude

+2

@HappyDude casting weg const ist wohldefiniert. Es wäre nur undefiniert, wenn Sie tatsächlich versuchen, in ein Objekt zu schreiben, das als "const" deklariert wurde (oder anderweitig nicht beschreibbar ist, z. B. ein String-Literal). –

9

Um als kompatibel betrachtet zu werden, sollte der Quellzeiger in der unmittelbar anterioren Indirektionsebene const sein. Also, dies wird Ihnen die Warnung in GCC:

char **a; 
const char* const* b = a; 

aber das wird nicht:

const char **a; 
const char* const* b = a; 

Alternativ können Sie es Stimmen:

char **a; 
const char* const* b = (const char **)a; 

Sie müssten die gleiche caste, um die Funktion f() wie bereits erwähnt aufzurufen. Soweit ich weiß, gibt es in diesem Fall keine Möglichkeit, eine implizite Konvertierung durchzuführen (außer in C++).

+0

Bitte lesen Sie die Frage genauer. Ich bin mir bewusst, dass das erste Code-Snippet falsch ist, wie ich in der darauf folgenden Zeile sage. Die zweite sollte jedoch in Ordnung sein (soweit ich das beurteilen kann). Wie ich bereits erwähnt habe, gibt MSVC keine Warnung, aber gcc tut es. – HappyDude

+0

Entschuldigung, ich habe nicht gut erklärt, was ich wollte, und auch nicht das Ergebnis in GCC getestet, um sicherzustellen, dass die Codebeispiele korrekt waren. Korrigieren. –

+0

Danke Fabio, deine bearbeitete Antwort ist viel klarer. Ich denke, ich werde nur akzeptieren müssen, dass ich es werfen muss. – HappyDude

1

Das ist ärgerlich, aber wenn Sie bereit sind, eine weitere Ebene der Umleitung hinzufügen möchten, können Sie oft folgendes nach unten drücken, in den Zeiger-to-Zeiger:

char c = 'c'; 
char *p = &c; 
char **a = &p; 

const char *bi = *a; 
const char * const * b = &bi; 

Es hat eine etwas andere Bedeutung, aber es ist in der Regel praktikabel, und es verwendet keine Besetzung.