Hallo zusammen
Ich verstehe momentan nicht, warum in C ein impliziter Cast von char **
zu const char ** eine Warnung erzeugt.
Ich möchte einer Funktion einen Zeiger auf ein Zeigerarray übergeben.
Manchmal sind die Strings, auf die die Zeiger im Array zeigen, konstant,
manchmal nicht (dynamisch erzeugt). Dazu habe ich folgendes
Minimalbeispiel erstellt:
1
voidfoo(constchar*const*ptr){
2
ptr=ptr;
3
}
4
5
intmain(void){
6
char**ptr;
7
8
foo(ptr);
9
10
return0;
11
}
Bei der Übergabe von ptr an foo erzeugt der Compiler folgende Warnung:
1
[Warning] passing argument 1 of 'foo' from incompatible pointer type [enabled by default]
2
[Note] expected 'const char * const*' but argument is of type 'char **'
Auch eine direkte Zuweisung bemängelt der Compiler:
1
intmain(void){
2
constchar**ptr1;
3
char**ptr2;
4
5
ptr1=ptr2;
6
7
return0;
8
}
Dies erzeugt folgende Warnung:
1
[Warning] assignment from incompatible pointer type [enabled by default]
Da der Compiler eine Warnung erzeugt, gehe ich davon aus, dass ein char
** nicht ohne Probleme zu einem const char ** umgewandelt werden kann
und somit nicht dasselbe ist.
Daraus ergeben sich für mich zwei Fragen:
1. Warum ist das so?
2. Wie kann ich das Problem umgehen?
Compiler: TDM-GCC 4.8.1 32-bit Release
Parameter: -Ofast -m32 -std=c99 -Wall -Wextra -pedantic
Eine Warnung ist wie der Name schon sagt eine Warnung.
Diese zu ignorieren muss nicht zu einem Fehler führen.
Schreibt ein Programmierer eine Funktion und deklariert einen oder
mehrere Parameter als konstant, so kann dies zwar aus langer Weile
heraus geschehen, oder weil copy and paste angesagt war, in den meisten
Fällen hat es aber einen Grund.
Ich betrachte solche Fälle als: "Guck's dir an" und Quittiere es durch
einen cast, so es nicht stört. Es kann aber nicht schaden, wenn man weis
was man tut – der cast ist nämlich eine Kanone, mit der man nicht
unbedingt auf Spatzen schießen sollte.
Oder anders ausgedrückt: Damit kann man aus Käse Wurst machen, ohne das
es jemand merkt.
Im ANSI-Standard steht:
Each argument shall have a type such that its value may be assigned to
an object with the unqualified version of the type of its corresponding
parameter.
D.h. ein (z.B.)
1
char*
ist an einen
1
constchar*
zuweisbar.
Ein
1
constchar*const*
ist aber eben kein qualified type von
1
char**
(sondern pointer to qualified type, daher resultiert die Warnung.
Die Zuweisung von
1
char**
an einen
1
char*const*
ist erlaubt (erfüllt die Bedingung).
Wozu das gut sein soll? Keine Ahnung. Wahrscheinlich nur um die Leute zu
verwirren.
be stucki schrieb:> Ich möchte einer Funktion einen Zeiger auf ein Zeigerarray übergeben.> Manchmal sind die Strings, auf die die Zeiger im Array zeigen, konstant,> manchmal nicht (dynamisch erzeugt).
Ob sie dynamisch erzeugt sind oder nicht, hat doch erstmal nichts damit
zu tun, ob sie als konstant zu betrachten sind.
Übrigens: So etwas wie "implizite Casts" gibt es nicht.
> 1. Warum ist das so?
Weil ein Zeiger auf const char nun mal ein anderer Typ ist als ein
Zeiger auf char.
> 2. Wie kann ich das Problem umgehen?
Das kann man anhand deiner Beispiele nicht sagen.
Vielen Dank für die hilfreichen Antworten!
Amateur schrieb:> Eine Warnung ist wie der Name schon sagt eine Warnung.> Diese zu ignorieren muss nicht zu einem Fehler führen.
Wenn ich wüsste, dass das Ignorieren dieser Warnung nicht zu einem
Fehler führen kann, hätte ich die Sache mit einem Cast erledigt.
Markus F. schrieb:> Im ANSI-Standard steht:> [...]
Jetzt erinnere ich mich auch wieder daran, sowas schon mal irgendwo
gehört zu haben. Daran wirds wohl liegen.
Rolf Magnus schrieb:> Ob sie dynamisch erzeugt sind oder nicht, hat doch erstmal nichts damit> zu tun, ob sie als konstant zu betrachten sind.
Richtig.
Yalu X. schrieb:> http://stackoverflow.com/questions/1404738/why-does-the-following-give-me-a-conversion-error-from-double-to-const-doubl
Danke für den Link. Damit kann ich was anfangen.
Folgender Code erzeugt keine Warnung:
1
voidfoo(constchar*const*ptr){
2
ptr=ptr;
3
}
4
5
intmain(void){
6
charStr1[]="abc";
7
charStr2[]="xyz";
8
constchar*constStrPtr[]={Str1,Str2,NULL};/* Das zweite const kann auch weggelassen werden */
9
10
foo(StrPtr);
11
12
return0;
13
}
Meiner Meinung nach ist das dasselbe wie ein Cast von char ** nach const
char const , nur anders geschrieben. Jetzt ist nur noch abzuklären,
wie viel hier meine Meinung zählt...
be stucki schrieb:> Meiner Meinung nach ist das dasselbe wie ein Cast von char ** nach const> char const , nur anders geschrieben. Jetzt ist nur noch abzuklären,> wie viel hier meine Meinung zählt...
Nö. Das ist definitiv zweimal genau derselbe Typ,
1
constchar*const*
und deswegen zuweisungskompatibel, da gibt's keine Regel anzuwenden
(außer der, daß dasselbe dasselbe ist ;) ).
Wenn Du das zweite const wegläßt, sind wir wieder beim Thema: ein
1
constchar**
ist zuweisungskompatibel zu einem
1
constchar*const*
oder z.B. auch zu einem
1
constchar*volatile*const
. Siehe oben. Qualified type.
Übrigens seh' ich da keinen cast und es geht auch gar nicht um casts.
Ich seh hier definitiv einen Cast.
Markus F. schrieb:> und es geht auch gar nicht um casts.
Korrekt. Es geht darum, warum der Code aus meinem ersten Posting eine
Warnung erzeugt. Momentan sehe ich den Unterschied zwischen den beiden
Fällen nicht:
1
voidfoo(constchar*const*ptr){
2
ptr=ptr;
3
}
4
5
intmain(void){
6
charstr1[]="abc";
7
charstr2[]="xyz";
8
9
/* Fall 1, keine Warnung */
10
constchar*ptr1[]={str1,str2,NULL};
11
foo(ptr1);
12
13
/* Fall 2, Warnung */
14
char*ptr2[]={str1,str2,NULL};
15
foo(ptr2);
16
17
return0;
18
}
Dass ein const char nicht dasselbe wie ein char ist, ist mit klar. Mir
ist auch klar, dass man aus einem char ohne Probleme ein const char
machen kann. Was mir nicht klar ist, ist warum ich aus einem char * zwar
ein const char * und danach ein const char **, aber aus einem char *
nicht direkt ein const char ** machen kann. Das Resultat wäre in beiden
Fällen dasselbe (hoffe ich jedenfalls).
be stucki schrieb:> Markus F. schrieb:>> Übrigens seh' ich da keinen cast>> char * ptr1;> const char * ptr2 = ptr1;> Ich seh hier definitiv einen Cast.
Da ist aber keiner. Du würdest auch keine Warnung bekommen, wenn da ein
Cast wäre.
be stucki schrieb:> Dass ein const char nicht dasselbe wie ein char ist, ist mit klar. Mir> ist auch klar, dass man aus einem char ohne Probleme ein const char> machen kann.
Kann man eigentlich nicht. Man kann aber ohne Cast einen const char*
drauf zeigen lassen.
> Was mir nicht klar ist, ist warum ich aus einem char * zwar> ein const char * und danach ein const char **, aber aus einem char *> nicht direkt ein const char ** machen kann. Das Resultat wäre in beiden> Fällen dasselbe (hoffe ich jedenfalls).
Wahrscheinlich einfach, weil die Regel, daß man auf ein X ohne Cast
einen const X* zeigen lassen kann, nicht rekursiv angewendet wird, wenn
X selbst wieder ein Zeiger ist.