const int* und const int**



  • Folgender Code wird nicht uebersetzt. Der Compiler sagt:

    error: invalid conversion from 'int**' to 'const int**'
    
    #include <iostream>
    using namespace std;
    
    void func(const int * ptr)
    {}
    
    void func0(const int** ptr)
    {}
    
    int main()
    {
    	int a(42);
    	int * ptr = &a;
    	int ** ptr0 = &ptr;
    	func(ptr);
            func0(ptr0);
    }
    

    Warum funktioniert die Umwandlung von int* nach const int* super, aber int** nach const int** nicht?



  • Beginner schrieb:

    Folgender Code wird nicht uebersetzt. Der Compiler sagt:

    error: invalid conversion from 'int**' to 'const int**'
    

    Warum funktioniert die Umwandlung von int* nach const int* super, aber int** nach const int** nicht?

    Grundsätzlich eine interessante Frage, denn ich sehe grade keinen Grund, warum das scheitert. (Getestet g++ 4.0.3)

    Ich äußere mal die wilde Vermutung, dass man programmiert hat, dass man zu const Werten konvertieren kann, aber einfach vergessen hat, die Mischformen zu berücksichtigen.
    Folgende Konstruktion arbeitet nämlich wieder einwandtfrei.

    void func0(const int * const * ptr) 
    {}
    

    Einen tieferen Grund sehe ich allerdings nicht, warum die Mischform 'const int **' ohne Cast nicht erlaubt ist.

    EDIT: Wenn man schon eine Frage stellt... sollte man auch gleich die Folgefrage angehen...

    void func0(int * const * ptr) 
    {}
    

    geht auch ohne cast...

    Offenbar castet gcc const nur auf das Ziel der ersten Dereferenzierung automatisch.



  • Hallo,
    da hierbei ein "logischer" Fehler vorliegt...

    int* nach const int*

    wo siehst du das? ich sehe einen pointer auf eine "änderbare" integer-variable.
    D.h. du kannst int über einen pointer ändern was ja erlaubt ist...

    aber...

    int ** ptr0 = &ptr;

    da ptr0 ja selbst ein const int** ist kannst du ja den wert über *ptr verändern - also kein const mehr 🙂

    Korregiert mich bitte wenn ich falsch liege 😉



  • codefrag schrieb:

    int ** ptr0 = &ptr;

    da ptr0 ja selbst ein const int** ist kannst du ja den wert über *ptr verändern - also kein const mehr 🙂

    Korregiert mich bitte wenn ich falsch liege 😉

    const int **
    ist ein variabler Zeiger auf einen variablen Zeiger, der auf ein konstantes Int zeigt.
    Du darfst den Zeiger auf den Zeiger ändern und den Zeiger auf ein konstantes Int. Aber von dem int hast Du bei diesem Datentyp die Finger zu lassen.

    Ich sehe hier keinen logischen Fehler?! Und hätte auch keine Skrupel von 'int **' nach 'const int **' zu casten, denn es schränkt 'int **' innerhalb
    der Funktion ja nur ein, was kein logischer Fehler sein kann.

    Vielleicht brauche ich eine deutlichere Erklärung, aber momentan sehe ich hier eine keinen logischen Grund für diese Compilermeldung.



  • Xin schrieb:

    Vielleicht brauche ich eine deutlichere Erklärung, aber momentan sehe ich hier eine keinen logischen Grund für diese Compilermeldung.

    Ich versuchs mal anders...

    void func0(const int** ptr)
    {}
    

    Mit const definierst du einen int pointer auf einen pointer dessen Inhalt keines Falls geändert werden darf.

    Da du den Inhalt aber über den "Ursprungspointer" ändern kannst ist const somit nicht mehr korrekt.

    Vllt. ein anderes Codebeispiel bei den man es besser nachvollziehen kann

    int a = 42;
    int* b = &a;
    const int** c = &b;  <<--- hier ist der Fehler
    

    Wenn du den Inhalt von b änderst, änderst du ja automatisch auch den wert von dem const wert c - und das ist nicht zulässig

    Ich glaube anders bekomm ichs nicht hin 😋





  • codefrag schrieb:

    Xin schrieb:

    Vielleicht brauche ich eine deutlichere Erklärung, aber momentan sehe ich hier eine keinen logischen Grund für diese Compilermeldung.

    Ich versuchs mal anders...

    void func0(const int** ptr)
    {}
    

    Mit const definierst du einen int pointer auf einen pointer dessen Inhalt keines Falls geändert werden darf.

    const int ** == variabler Zeiger auf variabler Zeiger auf Integer-Konstante.

    codefrag schrieb:

    Da du den Inhalt aber über den "Ursprungspointer" ändern kannst ist const somit nicht mehr korrekt.

    Okay, ich denke, ich habe was Du meinst soweit verstanden.
    int const ** gibt aber nur wieder, dass es einen Zeiger auf einen Zeiger auf eine Konstante gibt. Auf welche Konstante man zeigt, ist aber nicht festgelegt, sonst wäre es ein "int const * const * const".
    Diese Logik würde bedeuten, dass ein 'int const **' als Datentyp nicht erlaubt sein kann, da man das int ja in keinem Fall ändern dürfte. Der Datentyp wäre also ein Widerspruch in sich. 'int const **' ist aber ein akzeptierter Datentyp... also fehlt entweder hier eine Fehlermeldung im Compiler oder die Logik Deiner Beschreibung trifft nicht auf den GNU C++ zu.

    codefrag schrieb:

    Vllt. ein anderes Codebeispiel bei den man es besser nachvollziehen kann

    int a = 42;
    int* b = &a;
    const int** c = &b;  <<--- hier ist der Fehler
    

    Das ist grundsätzlich der gleiche Code, wie von _Beginner_ vorgegeben, nur dass es diesmal in eine lokale Variablen nicht gecastet wird, statt in ein Parameter einer funktion.

    Ich verstehe es also entweder nicht, oder ich verstehe es doch und die Meldung vom Compiler ist Blödsinn. Obwohl ich üblicherweise lange brauche, bevor ich an Compilern zweifle, bin ich hier mal so frei 😉



  • Xin schrieb:

    codefrag schrieb:

    Vllt. ein anderes Codebeispiel bei den man es besser nachvollziehen kann

    int a = 42;
    int* b = &a;
    const int** c = &b;  <<--- hier ist der Fehler
    

    Das ist grundsätzlich der gleiche Code, wie von _Beginner_ vorgegeben, nur dass es diesmal in eine lokale Variablen nicht gecastet wird, statt in ein Parameter einer funktion.

    Ich verstehe es also entweder nicht, oder ich verstehe es doch und die Meldung vom Compiler ist Blödsinn. Obwohl ich üblicherweise lange brauche, bevor ich an Compilern zweifle, bin ich hier mal so frei 😉

    Ja es ist "fast" der selbe Code nur etwas übersichtlicher, sonst wäre es ja ein anderes Problem 😉

    Aber du hast dir doch die Antwort schon selbst gegeben. Der Zeiger auf einen Zeiger darf nicht veränderbar sein, da er eben vom "typ" const ist. Das ist den Codebeispielen aber nicht der Fall da der Zeiger *b kein const ist, und somit der wert des zeiger zeigers (ohh gott was für n deutsch 😮 ) verändert werden könnte.

    so habe sowohl meinen code als auch mal den von _Beginner_ bei mir getestet. Er lässt sich übersetzen bringt jedoch eine Warnung (gcc 4.1.0):

    test.c: In function ‘main’:
    test.c:15: warning: passing argument 1 of ‘func0’ from incompatible pointer type

    bzw.

    test.c:8: warning: initialization from incompatible pointer type

    Es wäre deshalb gut zu wissen welchen Compiler _Beginner verwendet. Denn "logisch" ist es nicht korrekt 🙂 aber der gcc scheint es zu zulassen.



  • codefrag schrieb:

    Aber du hast dir doch die Antwort schon selbst gegeben. Der Zeiger auf einen Zeiger darf nicht veränderbar sein, da er eben vom "typ" const ist. Das ist den Codebeispielen aber nicht der Fall da der Zeiger *b kein const ist, und somit der wert des zeiger zeigers (ohh gott was für n deutsch 😮 ) verändert werden könnte.

    Hier ist kein Zeiger const, das einzige, was const zu sein hat, ist das int. Die Zeiger sind bei 'int const **' weiterhin beliebig.
    Ich habe mir den Link von TactX nun angeguckt und auch da fand ich 'The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure.'... zumindestens soweit, dass meine Logik dem nicht vollständig zu folgen vermochte.

    Wenn ich durch die Änderung des Zeigers verhindern wollte, dass das 'int const' einen anderen Wert erhält, weil der Zeiger dann nunmal auf eine andere Konstante zeigt, so hat das automatische casten zu einem 'int const * const *' nur genausowenig Sinn, da der Zeiger auf 'int const * const' weiterhin variabel bleibt. Damit könnte man auch auf einen anderen Zeiger zeigen könnte, der dann auch auf eine andere Konstante zeigen könnte. Das wäre dann wieder eine Verletzung dieser Logik.

    Weiterhin hat es keinen Sinn bei dieser Logik Variablen vom Typ 'int const **' überhaupt zu erlauben.

    Das ganze hakt in meinen Verständnis. Entweder erlaubt man das von 'int **' zu 'int ** const', 'int * const * const' oder 'int const * const * const', aber dann eben nicht zu 'int const * const *'.
    So hakt es.

    Damit das logisch bleibt, müsste die Auslegung klarer an der Aussage des Datentyps liegen, damit wäre ein casten auf 'const int **' weiterhin eine Einschränkung bezogen auf 'int **' und damit vollkommen vertretbar, wobei der Datentyp 'const int **' in seinem Sinn selbst fraglich bliebe. Aber das ist dann ja ein anderes Kapitel.



  • Xin schrieb:

    codefrag schrieb:

    Aber du hast dir doch die Antwort schon selbst gegeben. Der Zeiger auf einen Zeiger darf nicht veränderbar sein, da er eben vom "typ" const ist. Das ist den Codebeispielen aber nicht der Fall da der Zeiger *b kein const ist, und somit der wert des zeiger zeigers (ohh gott was für n deutsch 😮 ) verändert werden könnte.

    Hier ist kein Zeiger const, das einzige, was const zu sein hat, ist das int. Die Zeiger sind bei 'int const **' weiterhin beliebig.

    Ich habe mir den Link von TactX nun angeguckt und auch da fand ich 'The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure.'... zumindestens soweit, dass meine Logik dem nicht vollständig zu folgen vermochte.

    Ich glaube da haben wir jetzt aneinander vorbeigeredet 😉 es ist klar das kein Zeiger konstant sein kann ....

    Hmm aber der gcc lässt das zuweisen/verwenden der verschiedenen "typen" ja zu... also bei welchem Compiler bekommt man den "error: invalid conversion from 'int**' to 'const int**'"



  • codefrag schrieb:

    Hmm aber der gcc lässt das zuweisen/verwenden der verschiedenen "typen" ja zu... also bei welchem Compiler bekommt man den "error: invalid conversion from 'int**' to 'const int**'"

    Eine Aussage mit dem ich jetzt gar nicht gerechnet habe...

    In meinem Fall - wie oben beschrieben - mit dem GNU C-Compiler Version 4.0.3.

    Welche Version nutzt Du denn?



  • Xin schrieb:

    Welche Version nutzt Du denn?

    gcc 4.1.0

    Er bringt Warnings aber das Binary funktioniert 🙂 habe testweise Werte ausgeben und über die Pointer Werte manipuliert..

    test.c:15: warning: passing argument 1 of ‘func0’ from incompatible pointer type

    bzw.

    test.c:8: warning: initialization from incompatible pointer type



  • Ich habe mir mal die recht anregende Diskussion und die Begruendung der c-faq durchgelesen.

    const char c = 'x';	/* 1 */
    	char *p1;			   /* 2 */
    	const char **p2 = &p1;	/* 3 */
    	*p2 = &c;			  /* 4 */
    	*p1 = 'X';			/* 5 */
    

    Erst habe ich mich gefragt: Warum erlaubt der Compiler in Zeile 4, dass einem char* ein const char* zugewiesen wird? Dann habe ich gemerkt, dass *p2(also p1) dadurch dass Zeile 3 ausgefuehrt wurde nicht mehr nur ein char* ist, sondern in p2 als ein const char* aufgefasst wird.
    Die Speicherstelle(!) von p1 wird also unterschiedlich interpretiert. p1 selber in Zeile 2 glaubt, seinen Referenten modifizieren zu duerfen. *p2 in Zeile 4 hingegen glaubt, dass jene Speicherstelle ihren Referenten NICHT modifizieren darf. Diese Diskrepanz fuehrt dazu, dass man sich mit der Initialisierung aus Zeile 3 quasi "den Wolf im Schafspelz" ins Haus holt. In Zeile 4 faellt dann p2 auf diesen "Schafspelz" herein und verleiht p1 das Privileg auf einen const char zu zeigen, ohne selbst ein const char* zu sein, was sonst aber nur denen vorbehalten ist. Und genau um das zu vermeiden meckert der Compiler bei Zeile 3.

    Das daraus resultierende Dilemma ist zugegebenermassen recht unwahrscheinlich, jedoch so wie ich die Begruendung der c-faq verstehe soll bei einer const-Initialisierung(wie z.B. in Zeile 3) jeder denkbar moegliche Fall ausgeschlossen werden, dass irgendwie Schreibzugriff auf eine durch const geschuetzte variable erschlichen wird. Ein language feature, das zulaesst eine Variable vom Typ Pointer-auf-const-T mit einer Variablen vom Typ Pointer-auf-T zu initialisieren darf es nicht geben. Die einzige Ausnahme ist es, wenn T ein nicht-Pointer Typ ist.

    Das war jetzt z.T. etwas bildlich gesprochen, aber vielleicht wird es dadurch anschaulicher 🙂
    Mein Compiler ist uebrigens ein

    UniX:~$ g++ -v
    Using built-in specs.
    Target: i486-linux-gnu
    Configured with: ../src/configure -v --enable-languages=c,c++,fortran,objc,obj-c++,treelang --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --program-suffix=-4.1 --enable-__cxa_atexit --enable-clocale=gnu --enable-libstdcxx-debug --enable-mpfr --with-tune=i686 --enable-checking=release i486-linux-gnu
    Thread model: posix
    gcc version 4.1.2 20060729 (prerelease) (Debian 4.1.1-10)
    

    Die c-faq wies ja darauf hin, dass

    C++ would still not allow assigning a char ** to a const char **

    von daher kann es gut sein, dass der gcc den Code uebersetzt, der g++ hingegen nicht.

    So hab ich die Sache verstanden.


Log in to reply