2009-05-11 5 views
1

Le code suivant met en garde contre un type incompatible. Quelle est la bonne façon de résoudre ce code?Tableau dynamique de structures et appel de fonction f (const struct_type * const data [])

grâce

typedef struct a_struct struct_type; 

void f(const struct_type *const data[], unsigned n); 

void test(unsigned n) 
{ 
    struct_type **v; 

    assert(n); 
    v = malloc(n * sizeof(struct_type *)); 
    /* fill v with new struct_type (with a malloc per entry) */ 
    f(v, n); <- WARN on v 
} 
+2

Sur quelle ligne obtenez-vous l'avertissement? – LeopardSkinPillBoxHat

Répondre

4

La raison pour laquelle le compilateur se plaint est la première déclaration const dans f.

Essayez d'utiliser

void f(struct_type *const data[], unsigned n); 
/*...*/ 
f(v, n); 

et vous ne serez pas le même avertissement. Sinon, vous pouvez jeter v lorsque vous appelez f

void f(const struct_type *const data[], unsigned n); 
/*...*/ 
f((const struct_type * const *) v, n); 

C'est un peu contre-intuitif, mais en C, vous ne pouvez pas passer un pointer-to-pointer-to-nonconst pour un pointer-to-pointer-to-const. Ils ont fait une exception spéciale pour vous permettre de passer un pointer-to-nonconst pour un pointer-to-const.

Voici une question FAQ "Why can't I pass a char ** to a function which expects a const char **?":

Vous pouvez utiliser un pointer-to-T (pour tout type T) où un pointer-to-const-T devrait. Cependant, la règle (une exception explicite) qui autorise de légères discordances dans les types de pointeurs qualifiés n'est pas appliquée de manière récursive, mais uniquement au niveau supérieur. (const char ** est pointer-to-pointer-to-const-char, et l'exception ne s'applique pas.)

La raison pour laquelle vous ne pouvez pas attribuer une valeur char ** à un pointeur const char ** est quelque peu obscur. Étant donné que le qualificatif const existe, le compilateur souhaite vous aider à tenir vos promesses de ne pas modifier les valeurs const. C'est pourquoi vous pouvez attribuer un char * à un const char *, mais pas l'inverse: il est clairement sûr de ajouterconst -ness à un pointeur simple, mais il serait dangereux de l'enlever. Cependant, supposons que vous avez effectué la série de missions plus complexes suivantes:

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

Dans la ligne 3, nous attribuons un char ** à un const char **. (Le compilateur devrait se plaindre.) Dans la ligne 4, nous affectons const char * à const char *; C'est clairement légal. À la ligne 5, nous modifions ce que char * fait valoir - ceci est censé être légal. Cependant, p1 finit par pointer vers c, qui est const. Cela est arrivé à la ligne 4, parce que * p2 était vraiment p1. Cela a été mis en place à la ligne 3, qui est une affectation d'un formulaire qui n'est pas autorisé, et c'est exactement la raison pour laquelle la ligne 3 est refusée.

L'attribution d'un char ** à un const char ** (comme dans la ligne 3, et dans la question d'origine) n'est pas immédiatement dangereux. Mais il met en place une situation dans laquelle la promesse de p2 - que la valeur finalement pointée ne sera pas modifiée - ne peut pas être conservée.

(C++ a des règles plus complexes pour attribuer const pointeurs aux qualifiés qui vous permettent de faire plusieurs types de missions sans encourir des avertissements, mais toujours protéger contre toute tentative accidentelle de modifier les valeurs const. C++ ne permettent toujours pas l'attribution d'un char ** à un const char ** , mais il vous permettra de sortir avec l'attribution d'un char ** à un const char * const *.)

En C, si vous devez céder ou passer des pointeurs qui ont discordances qualification à autre que le premier niveau d'indirection, vous devez utiliser des moulages explicites (par exemple (const char **) dans ce cas), bien que, comme toujours, la nécessité d'un tel plâtre peut indiquer une profondeur problème que la distribution ne corrige pas vraiment.

Références: ISO Sec. 6.1.2.6, Sec. 6.3.16.1, Sec. 6.5.3 H & S Sec. . 7.9.1 pp 221-2

+0

Ça y est, cool – Andomar

0

Modifié basé sur la réponse de raiponce. Le problème est avec la double const dans la déclaration de f().

Code avec l'avertissement:

struct_type ** v; 
v = (struct_type **)malloc(10 * sizeof(struct_type *));  
f(v); 

Cette compile sans avertissement:

const struct_type *const* v; 
v = (const struct_type **)malloc(10 * sizeof(struct_type *)); 
f(v); 
-1

f espère obtenir en entrée un tableau de pointeurs (const struct_type * []). Vous passez un pointeur sur un pointeur de struct (const struct_type **).

La meilleure chose à faire, l'OMI, est de changer la signature de f à:

void f(const struct_type *const* data); 

Pourquoi avez-vous besoin de passer des tableaux comme arguments à des fonctions?

+0

Parce que c'est déclaré comme ça :) (c'est plus facile en cas de déclaration statique de données [] je suppose) – elmarco

+0

En C, "void ** myvar" et "void * myvar []" ne font qu'un. – Andomar

+0

désolé, mais ce n'est vraiment pas une bonne réponse à la question :) -1 pour le tri des réponses – elmarco

0

Voir si cela fonctionne pour vous:

f(struct_type *data); 

void test(unsigned n) 
{ 
    struct_type *v = malloc(n * sizeof(struct_type *)); 
    f(v); 
} 

S'il vous plaît laissez-moi savoir comment vous allez.

+0

Malheureusement, ça ne m'aidera pas, car j'ai besoin de le modifier pour remplir les structures. – elmarco

+0

Vous pouvez toujours le modifier lors du remplissage de la structure. Je me suis récemment battu avec un problème très similaire et mon code ressemblait à: void clearScrorer (SCORER * scorerArray) { \t scorerArray-> name = NULL; \t scorerArray-> score = -1; } Et cela a fonctionné sans aucun problème. Rappelez-vous qu'en faisant f (v) vous passez la référence à v, donc si vous modifiez v à l'intérieur de f, la fonction v ailleurs sera également modifiée. – Artur

Questions connexes