Forum: Compiler & IDEs [C] Cast von char** zu const char**


von B. S. (bestucki)


Lesenswert?

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
void foo(const char * const * ptr){
2
  ptr = ptr;
3
}
4
5
int main(void){
6
  char ** ptr;
7
8
  foo(ptr);
9
  
10
  return 0;
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
int main(void){
2
  const char ** ptr1;
3
  char ** ptr2;
4
  
5
  ptr1 = ptr2;
6
  
7
  return 0;
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

von Amateur (Gast)


Lesenswert?

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.

von Programmierer (Gast)


Lesenswert?

Sehr interessant. Kompiliert man diesen Code als C++, gibts keine 
Warnung. Liegt vermutlich daran dass C++ "const" sinnvoll(er) 
behandelt...

von Markus F. (mfro)


Lesenswert?

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
const char *
 zuweisbar.

Ein
1
const char * 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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Hier sind im 2. Beitrag die Gründe für die Warn- bzw. Fehlermeldungen
und insbesondere auch die diesbezüglichen Unterschiede zwischen C und
C++ genau erklärt:

  http://stackoverflow.com/questions/1404738/why-does-the-following-give-me-a-conversion-error-from-double-to-const-doubl

von B. S. (bestucki)


Lesenswert?

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
void foo(const char * const * ptr){
2
  ptr = ptr;
3
}
4
5
int main(void){
6
  char Str1[] = "abc";
7
  char Str2[] = "xyz";
8
  const char * const StrPtr[] = {Str1, Str2, NULL}; /* Das zweite const kann auch weggelassen werden */
9
  
10
  foo(StrPtr);
11
  
12
  return 0;
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...

von Markus F. (mfro)


Lesenswert?

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
const char * 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
const char **
 ist zuweisungskompatibel zu einem
1
const char * const *
 oder z.B. auch zu einem
1
const char * volatile * const
. Siehe oben. Qualified type.

Übrigens seh' ich da keinen cast und es geht auch gar nicht um casts.

von B. S. (bestucki)


Lesenswert?

Markus F. schrieb:
> Übrigens seh' ich da keinen cast
1
char * ptr1;
2
const char * ptr2 = ptr1;
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
void foo(const char * const * ptr){
2
  ptr = ptr;
3
}
4
5
int main(void){
6
  char str1[] = "abc";
7
  char str2[] = "xyz";
8
  
9
  /* Fall 1, keine Warnung */
10
  const char * ptr1[] = {str1, str2, NULL};
11
  foo(ptr1);
12
  
13
  /* Fall 2, Warnung */
14
  char * ptr2[] = {str1, str2, NULL};
15
  foo(ptr2);
16
  
17
  return 0;
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).

von Rolf Magnus (Gast)


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

be stucki schrieb:
> Markus F. schrieb:
>> Übrigens seh' ich da keinen cast
>
1
char * ptr1;
2
> const char * ptr2 = ptr1;
> Ich seh hier definitiv einen Cast.
>
ich nicht. Da ist kein cast. Ein cast wäre z.B.
1
const char * ptr2 = (const char *) ptr1;

und: casten kannst Du (fast) alles auf (fast) alles andere (auch wenn's 
ganz und gar nicht immer empfehlenswert ist).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.