2009-11-08 5 views
5

J'ai un morceau de code C qui ressemble à ceci:coulée pointeur vide const tableau de pointeurs const char correctement dans C

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

maintenant bar() est une fonction qui renvoie une (const void *). Comment puis-je diffuser correctement ce pointeur const? Le code produit cet avertissement 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 pas me débarrasser des avertissements par coulée correctement la valeur de retour.

EDIT: Ceci a été suggéré:

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

Il 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(). Le const dans la définition (const char(*)[2]) fait référence aux éléments du tableau, pas le pointeur vers le tableau. Cette définition de type est essentiellement un tableau qui, lorsqu'il est représenté par un pointeur void, est et nonconst. La vraie réponse est qu'un (const void *) perd son cons t-ness lorsqu'il est converti en (const char (*)[2]).

+2

Le titre de votre question indique "tableau de pointeurs char char", mais votre devoir est de pointer vers un tableau de deux caractères. Pouvez-vous clarifier exactement quelle barre est censée revenir? –

+0

Quelle version de gcc et quelles options d'avertissement utilisez-vous? –

+0

GCC 4.4.1 avec -Wall – postfuturist

Répondre

6

Plusieurs autres ont déclaré la distribution correcte, mais il génère un spurious warning. Cet avertissement provient d'un possible bug dans la norme C, ou (selon votre interprétation) un cas que GCC devrait traiter spécialement. Je crois que le qualificateur const peut être levé de manière sûre et non ambiguë vers le type de tableau. Vous pouvez laisser tomber cet avertissement avec -Wno-cast-qual mais bien sûr cela éliminera les avertissements pour les cas qui vous intéressent réellement.

Pour élaborer, le type const char (*)[2] signifie «pointeur vers tableau (longueur 2) de constchar». Le tableau n'est pas marqué const, seulement les éléments du tableau.Par rapport au type const void *, le compilateur remarque que ce dernier est un pointeur vers const, alors que le premier ne l'est pas, générant ainsi l'avertissement. Le standard C ne fournit aucun moyen de marquer un tableau comme const, même si un tableau const serait équivalent à un tableau de const.

+0

J'ai compilé [ce code] (http://ideone.com/UmuOdJ) et je n'ai pas reçu un tel avertissement. – haccks

+0

@haccks Génère un avertissement avec 'gcc -Wcast-qual'. – Jed

3

Essayez:

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

Modifier mais si bar retourne un pointeur vers un tableau const de pointeurs de char comme vos conseils de titre de la question, il devrait y avoir pas besoin d'un casting si vous attribuez à une variable ce type:

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

Juste une note, vous n'avez pas besoin les 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 il peut être difficile pour certains à lire:

cdecl> explain const char (*foo)[2] 
declare foo as pointer to array 2 of const char 
+1

S'il * sait * que le pointeur de retour renvoie effectivement un pointeur sur un tableau de tableaux de 2 caractères, plutôt qu'un simple pointeur sur un tableau de 2 caractères, alors en conservant les dimensions du tableau, il peut effectuer une arithmétique de pointeur valide '. –

+0

Bon point je suppose, mais cela ne serait utile que s'il avait un tableau 2D de ces choses allouées dans un bloc contigu de mémoire et qu'il connaissait d'une autre source les limites de ce tableau 2D par rapport au pointeur retourné. Ne semble pas probable dans ce cas. D'un autre côté, omettre la dimension ne lui achète probablement rien, c'est la plupart du temps juste des anecdotes, je suppose. En passant, il renvoie un pointeur vers un tableau de 2 caractères, pas un tableau de tableaux de 2 caractères. Ce serait 'const char (* foo) [] [2]' ou cdecl dit: declare foo comme pointeur vers le tableau de tableau 2 de const char –

+0

Les tableaux sont généralement passés par des pointeurs vers leur premier élément, donc pourquoi 'const char (* foo) [2] 'pourrait pointer vers un tableau de tableaux de 2 caractères. Ou un exemple plus familier: les chaînes terminées '\ 0' sont généralement renvoyées en tant que' char * ', et non' char (*) [] '. –

3

Le problème avec l'avertissement dans la dernière version de la distribution a des racines historiques. Vous savez que le langage C (ainsi que C++) interdit correctement la conversion T** -> const T**. Ceci est correct, puisque permettre cette conversion ouvrirait la voie à quelques violations subtiles des règles de const-correction (peut être trouvé dans n'importe quelle FAQ qui se respecte).

Cependant, le langage C interdit également la conversion T** -> const T* const*. Ceci est différent de C++, ce qui permet cette conversion. (Cette conversion ne viole pas la const-correction.) 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 continue à persister en C (même en C99). Franchement, je ne sais pas pourquoi il est resté inchangé dans C99. Un des "retombées" de ce défaut (ou plus précisément de cette approche de traitement de la const-correction) est qu'en C, la conversion T (*)[N] -> const T (*)[N] reste également interdite, même si elle ne comporte aucune menace inhérente à la const-correction.

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 distribution explicite, l'avertissement GCC est totalement injustifié. Vous pouvez essayer de contourner l'avertissement en utilisant un cast chaîné

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