2014-06-12 8 views
5

Ich habe eine einfache Frage zu GLib.Warum verwendet GLib in diesen Funktionen nicht 'const'?

Ich habe den folgenden Code:

static const char *words[] = { "one", "two", "three", NULL }; 

void main() { 
    puts(g_strjoinv("+", words)); 
} 

Dieser Code druckt one+two+three. Es verwendet eine GLib-Funktion, die Strings verbindet.

Die signature dieser Funktion ist:

char *g_strjoinv (const char *separator, char **str_array); 

(. Um genau zu sein, GLib verwendet gchar, nicht char, aber lassen Sie uns dies ignorieren)

Nun frage ich mich, warum der Parameter char **str_array und nicht . Es zwingt mich eine explizite Umwandlung zu tun der Compiler Warnung loszuwerden („erwartet‚char **‘aber Argument ist vom Typ‚const char **‘“):

puts(g_strjoinv("+", (char **)words)); 

Ich sehe in GLib's reference und ich siehe alle die Funktionen dort sind so definiert: sie akzeptieren char **, nicht const char **.

Warum ist das? Warum verwendet GLib const char ** nicht?

Die Notwendigkeit, einen expliziten Cast zu verwenden, um const loszuwerden, macht meinen Code weniger sicher (weil der Compiler nicht mehr die Kompatibilität der Argumente überprüft). Es macht mich auch nervös, weil GLib keinen Vertrag unterschreibt, dass meine Daten nicht geändert werden.

+1

Große Frage, str_array wird sicherlich nicht geändert (?), In diesem Fall würde const Sinn machen. – this

+0

@self: Ja, und es ist kein Problem mit nur dieser einen Funktion. Es ist in * all * GLibs Funktionen, die ein Array von Strings akzeptieren. Ich denke, sie haben einen guten Grund dafür und ich möchte wissen, was es ist. –

+3

Sie können sich [diese Frage] (http://c-faq.com/ansi/contmismatch.html) ansehen. Wenn du ein normales 'char **' hättest, was durchaus möglich ist, könntest du es auch nicht an eine Funktion übergeben, die 'const char **' ohne eine Umwandlung erwartet, also würdest du kein Problem lösen es als solches deklarieren. –

Antwort

6

Die Annahme in Frage, dass, wenn das zweite Argument von g_strjoinv() als Typ const char **, erklärt wurde, dann können Sie genauso gut ein const char ** oder ein char ** es passieren entweder.

Leider ist dies nicht wahr. Wie in erläutert, ist es OK, einen Zeiger auf T (für jeden Typ T) zu verwenden, wo ein Zeiger auf const T erwartet wird, aber nur wo die fehlende Übereinstimmung auf der höchsten Ebene der Indirektion ist. Das heißt, Sie können eine char * übergeben, wo eine const char * erwartet wird, aber Sie können nicht (ohne eine Besetzung) eine char ** übergeben, wo eine erwartet wird, weil in diesem Fall die Abweichung auf der zweiten Ebene der Indirektion ist. Die verknüpfte Frage erklärt in einigen Details warum es funktioniert auf diese Weise, die "etwas obskur" ist.

Also das Ergebnis ist dort gibt es keinen Typ (in C), der sowohl eine char ** als auch eine const char ** akzeptiert, ohne dass mindestens eine von ihnen eine Besetzung erfordert. Da dies der Fall ist, gibt es wahrscheinlich nicht viel Grund, etwas anderes als Bequemlichkeit zu bevorzugen, und die Bibliotheksautoren haben sich offensichtlich dafür entschieden, mit dem ersteren zu gehen. Als reine Spekulation würde ich vermuten, dass der Wunsch, eine char ** zu passieren, etwas häufiger ist als ein const char ** zu passieren, und daher ist die von ihnen gewählte Option wahrscheinlich bequemer.

In Ihrem Fall können Sie einfach den const Qualifier aus der Definition Ihres Arrays löschen.Es mag wie eine schlechte Form aussehen, wenn Sie const mögen, aber da Sie ein Array von Zeigern haben, die auf Zeichenfolgenliterale zeigen, wird Ihr Programm überwältigend wahrscheinlich sich beschweren und scheitern, wenn irgendetwas versucht, sie trotzdem zu schreiben, also sind Sie es nicht wirklich die Sicherheit in diesem speziellen Fall in irgendeiner sinnvollen Weise verlieren. Obwohl es ein undefiniertes Verhalten ist, um sie zu ändern, haben String-Literale in C Array char, nicht Array von const char (im Gegensatz zu C++).

Etwas mehr obskur, aber selbst wenn Sie es als übergeben könnten, könnte die Funktion hier noch die Zeiger ändern, auch wenn sie nicht ändern konnte, worauf sie zeigen, und Chaos auf eine andere Weise verursachen, so das Versprechen Sie, dass die Funktion, die noch gemacht wird, nicht garantieren würde, dass das, was Sie an die Funktion übergeben, nicht in irgendeiner Weise geändert wird.

+0

Das ist eine ** ausgezeichnete ** Antwort, vielen Dank! Ich war schon vorher auf dieses Problem gestoßen, wusste aber nicht, dass es auch mit diesem Fall zusammenhing. Jetzt, dank dir, verstehe ich es vollständig. BTW, du sagst, dass ich meine "const" einfach fallen lassen kann, aber wenn ich das mache, warnt GCC mich ("initialization discards 'const' ..."), weil ich glaube, dass String-Literale als Konstanten betrachtet werden. –

+0

(Übrigens danke auch für den letzten Absatz. Meine C-Fähigkeiten sind ein wenig eingerostet und ich brauchte diese Korrektur.) –

+0

Das klingt wie eine falsche Warnung, bist du sicher, dass du das nicht als C++ kompilierst, oder mit einer Erweiterung ? Kompilieren als Standard C sollte Ihnen diese Warnung nicht geben. –