Conversion du pointeur void const en un tableau de pointeurs de caractères const correctement en C

J’ai un morceau de code C qui ressemble à ceci:

const char (*foo)[2] = bar(); 

Maintenant, bar() est une fonction qui retourne un (const void *) . Comment lancer correctement ce pointeur const ? Le code produit cet avertissement de GCC:

 "initialization discards qualifiers from pointer target type". 

Voici quelques-unes de mes tentatives infructueuses:

 const char (*foo)[2] = (const char *)bar(); const char (*foo)[2] = (const void **)bar(); 

Le code original fonctionne, je ne peux tout simplement pas me débarrasser des avertissements en convertissant correctement la valeur de retour.

EDIT: Cela a été suggéré:

 const char (*foo)[2] = (const char (*)[2])bar(); 

Cela semble correct, mais GCC donne cet avertissement:

 "cast discards qualifiers from pointer target type" 

qui est presque identique à l’avertissement d’origine.

EDIT 2: OK, je pense que je l’ai. Le vrai problème ici est la définition ( const void * ) de bar() . La const dans la définition (const char( * )[2]) fait référence aux éléments du tableau, pas au pointeur sur le tableau. Cette définition de type est essentiellement un tableau qui, lorsqu’il est représenté par un pointeur void n’est pas const . La vraie réponse est qu’un ( const void * ) perd son contenu lorsqu’il est converti en (const char ( * )[2]) .

Plusieurs autres ont indiqué la dissortingbution correcte, mais cela génère un avertissement fallacieux . Cet avertissement provient d’un bogue possible dans la norme C ou (selon votre interprétation) d’un cas que GCC devrait traiter spécialement. Je crois que le qualificatif const peut être élevé sans risque et sans ambiguïté au type de tableau. Vous pouvez supprimer cet avertissement avec -Wno-cast-qual mais, bien sûr, cela éliminera les avertissements pour les cas qui vous intéressent.

Pour élaborer, le type const char (*)[2] signifie “pointeur sur le tableau (longueur 2) de const char “. Le tableau n’est pas marqué const , mais uniquement les éléments du tableau. Lorsque comparé au type const void * , le compilateur remarque que ce dernier est un pointeur sur const , alors que le premier ne l’est pas, générant ainsi l’avertissement. La norme C ne fournit aucun moyen de marquer un tableau en tant que const , même si un tableau const serait équivalent à un tableau de const .

Essayer:

 const char (*foo)[2] = (const char (*)[2])bar(); 

Modifiez mais si bar renvoie un pointeur sur un tableau const de pointeurs de caractères, comme l’indique le titre de votre question, il ne devrait pas être nécessaire de recourir à un transtypage si vous affectez une variable de ce type:

 char* const* foo = bar(); 

Le problème avec l’avertissement dans la dernière version de la dissortingbution a des racines historiques. Vous savez que le langage C (ainsi que C ++) interdit correctement la conversion T** -> const T** . Ceci est correct, car autoriser cette conversion ouvrirait la voie à des violations subtiles des règles const-correctness (que l’on peut trouver dans les FAQ qui se respectent bien).

Cependant, le langage C interdit également la conversion T** -> const T* const* . Ceci est différent de C ++, qui permet cette conversion. (Cette conversion ne viole pas const-correctness.) Cela a longtemps été considéré comme un “défaut de conception” dans la spécification du langage C. Ce défaut a été “corrigé” en C ++, mais il persiste en C (même en C99). Franchement, je ne sais pas pourquoi cela a été laissé inchangé dans C99. Une des “retombées” de ce défaut (ou plus précisément de cette approche du traitement de la correction de const) est que, dans le langage C, la conversion T (*)[N] -> const T (*)[N] rest également interdite. , même s’il ne présente aucune menace inhérente pour const-correct.

Je ne peux pas reproduire l’avertissement que vous recevez avec ma version de GCC. Mais si vous l’obtenez, cela semble être juste une autre conséquence de la même idéologie. Si vous tenez compte du fait que la conversion a été demandée par un opérateur de dissortingbution explicite, l’avertissement GCC est totalement injustifié. Vous pouvez essayer de contourner l’avertissement en utilisant une dissortingbution chaînée.

 const char (*foo)[2] = (const char (*)[2]) (void *) bar(); 

Juste une note, vous n’avez pas besoin des dimensions du tableau:

 const void *bar() { static const char a[10] = "abcdefghij"; return &a[4]; } int main() { const char (*foo)[2] = (const char (*)[])bar(); return 0; } 

Comme cela peut être difficile à lire pour certains:

 cdecl> explain const char (*foo)[2] declare foo as pointer to array 2 of const char 
 const char (*foo)[2] = (const char (*)[2])bar();