Solution pour "déréférencer le pointeur" vide * "& # 8221; avertissement dans struct en C?

StackOverflow https://stackoverflow.com/questions/289329

  •  08-07-2019
  •  | 
  •  

Question

J'essayais de créer une pseudo super structure pour imprimer un tableau de structures. Ma base les structures sont les suivantes.

/* Type 10 Count */
typedef struct _T10CNT
{
    int _cnt[20];
} T10CNT;

...

/* Type 20 Count */
typedef struct _T20CNT
{
    long _cnt[20];
} T20CNT;
...

J'ai créé la structure ci-dessous pour imprimer le tableau des structures susmentionnées. J'ai eu une erreur de déréférencement du pointeur vide lors de la compilation de l'extrait de code ci-dessous.

typedef struct _CMNCNT
{
    long  _cnt[3];
} CMNCNT;

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    int ii;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];
        fprintf(stout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

T10CNT struct_array[10];
...
printCommonStatistics(struct_array, NELEM(struct_array), sizeof(struct_array[0]);
...

Mon intention est d’avoir une fonction commune pour imprimer tous les tableaux. Faites-moi savoir la bonne façon de l'utiliser.

Appréciez l'aide à l'avance.

Edition: le nom du paramètre est remplacé par cmncnt par cmncntin. Désolé, c'était une erreur de frappe.

Merci, Mathew Liju

Était-ce utile?

La solution

Je pense que votre projet va échouer, mais je ne suis pas non plus convaincu que les autres réponses que je vois tienne pleinement compte des raisons plus profondes pour lesquelles.

Il semble que vous essayiez d'utiliser C pour traiter les types génériques, quelque chose qui devient toujours poilu. Vous pouvez le faire, si vous faites attention, mais ce n’est pas facile, et dans ce cas, je doute que cela en vaille la peine.

Raison plus profonde : supposons que nous dépassions les simples problèmes syntaxiques (ou à peine plus que syntaxiques). Votre code indique que T10CNT contient 20 int et que T20CNT contient 20 long . Sur les machines 64 bits modernes - autres que sous Win64 - sizeof (long)! = Sizeof (int) . Par conséquent, le code dans votre fonction d'impression doit faire la distinction entre les tableaux int de déréférencement et les tableaux de longue durée . En C ++, il existe une règle selon laquelle vous ne devez pas essayer de traiter les tableaux de manière polymorphe, et c'est ce genre de chose qui explique pourquoi. Le type CMNCNT contient 3 valeurs long ; différent des structures T10CNT et T20CNT, bien que le type de base du tableau corresponde à T20CNT.

Recommandation de style : Je vous recommande vivement d'éviter les caractères de soulignement soulignés. En général, les noms commençant par un trait de soulignement sont réservés à l'implémentation à utiliser et à utiliser comme macros. Les macros n'ont aucun respect pour la portée; si l'implémentation définit une macro _cnt, votre code serait détruit. Il y a des nuances à quels noms sont réservés; Je ne suis pas sur le point d'entrer dans ces nuances. Il est beaucoup plus simple de penser que les noms commençant par un soulignement sont réservés et que cela vous évitera des ennuis.

Suggestion de style : votre fonction d'impression renvoie un résultat positif sans condition. Ce n'est pas raisonnable votre fonction ne doit rien renvoyer, afin que l'appelant n'ait pas à tester le succès ou l'échec (car il ne peut jamais échouer). Un codeur attentif qui observe que la fonction renvoie un statut testera toujours le statut de retour et aura un code de traitement des erreurs. Ce code ne sera jamais exécuté, il est donc mort, mais il est difficile à quiconque (ou au compilateur) de le déterminer.

Correction de surface : Temporairement, nous pouvons supposer que vous pouvez traiter int et long comme des synonymes; mais vous devez sortir de l’habitude de penser qu’ils sont synonymes, cependant. L'argument void * est le moyen correct de dire "cette fonction prend un pointeur de type indéterminé". Toutefois, dans la fonction, vous devez convertir un void * en un type spécifique avant de procéder à l'indexation.

typedef struct _CMNCNT
{
    long    count[3];
} CMNCNT;

static void printCommonStatistics(const void *data, size_t nelem, size_t elemsize)
{
    int i;
    for (i = 0; i < nelem; i++)
    {
        const CMNCNT *cmncnt = (const CMNCNT *)((const char *)data + (i * elemsize));
        fprintf(stdout,"STATISTICS_INP: %ld\n", cmncnt->count[0]);
        fprintf(stdout,"STATISTICS_OUT: %ld\n", cmncnt->count[1]); 
        fprintf(stdout,"STATISTICS_ERR: %ld\n", cmncnt->count[2]);
    }
}

(J'aime bien l'idée d'un flux de fichiers appelé stout . Suggestion : utilisez du copier / coller sur du code source réel - c'est plus sûr! I ' En général, j'utilise " sed 's / ^ / /' fichier.c "pour préparer le code à copier-coller dans une réponse SO.)

Quel est le rôle de cette ligne? Je suis content que vous ayez demandé ...

  • La première opération consiste à convertir le const void * en un const char * ; Cela vous permet d'effectuer des opérations de taille en octets sur l'adresse. Dans les jours précédant la norme C, char * était utilisé à la place de void * en tant que mécanisme d'adressage universel.
  • L'opération suivante ajoute le nombre correct d'octets à atteindre au début du i élément du tableau d'objets de taille elemsize .
  • La deuxième distribution indique ensuite au compilateur "Faites-moi confiance - Je sais ce que je fais" et "considérons cette adresse comme l'adresse d'une structure CMNCNT".

À partir de là, le code est assez simple. Notez que puisque la structure CMNCNT contient une valeur long , j'ai utilisé % ld pour indiquer la vérité à fprintf () .

Etant donné que vous n'êtes pas sur le point de modifier les données dans cette fonction, utiliser le qualificatif const comme je l'ai fait n'est pas une mauvaise idée.

Notez que si vous allez

Autres conseils

Le type de vide est délibérément laissé incomplet. Il en résulte que vous ne pouvez pas déréférencer les pointeurs vides, ni vous ne pouvez en prendre la taille. Cela signifie que vous ne pouvez pas utiliser l'opérateur indice en l'utilisant comme un tableau.

Dès que vous assignez quelque chose à un pointeur vide, toute information de type de l'original pointé sur le type est perdue. Vous ne pouvez donc déréférencer la référence si vous la rediffusez pour la première fois sur le type de pointeur d'origine.

D'abord et le plus important, vous passez T10CNT * à la fonction, mais vous essayez de transtyper (et de déréférencer) que CMNCNT * dans votre fonction. Ce n'est pas un comportement valide et indéfini.

Vous avez besoin d’une fonction printCommonStatistics pour chaque type d’éléments de tableau. Alors, avoir un printCommonStatisticsInt , printCommonStatisticsLong , printCommonStatisticsChar qui diffèrent tous par leur premier argument (l'un prenant int * , l'autre prenant < code> long * , etc.). Vous pouvez les créer à l'aide de macros afin d'éviter un code redondant.

Passer la structure elle-même n'est pas une bonne idée, car vous devez alors définir une nouvelle fonction pour chaque taille différente du tableau contenu dans la structure (car ils sont tous de types différents). Il vaut donc mieux passer directement le tableau contenu ( struct_array [0] ._ cnt , appelez la fonction pour chaque index)

Modifiez la déclaration de la fonction en char * comme suit:

static int printCommonStatistics(char *cmncnt, int cmncnt_nelem, int cmncnt_elmsize)

le type vide n'assume aucune taille particulière alors qu'un caractère prendra une taille d'octet.

Vous ne pouvez pas faire ceci:

cmncnt->_cnt[0]

si cmnct est un pointeur vide.

Vous devez spécifier le type. Vous devrez peut-être repenser votre mise en œuvre.

La fonction

static int printCommonStatistics(void *cmncntin, int cmncnt_nelem, int cmncnt_elmsize)
{
    char *cmncntinBytes;
    int ii;

    cmncntinBytes = (char *) cmncntin;
    for(ii=0; ii<cmncnt_nelem; ii++)
    {
        CMNCNT *cmncnt = (CMNCNT *)(cmncntinBytes + ii*cmncnt_elmsize);  /* Ptr Line */
        fprintf(stdout,"STATISTICS_INP: %d\n",cmncnt->_cnt[0]);
        fprintf(stdout,"STATISTICS_OUT: %d\n",cmncnt->_cnt[1]); 
        fprintf(stdout,"STATISTICS_ERR: %d\n",cmncnt->_cnt[2]);
    }
    return SUCCESS;
}

Fonctionne pour moi.

Le problème est que, sur la ligne, il est commenté "Ptr Line". le code ajoute un pointeur sur un entier. Puisque notre pointeur est un caractère *, nous avançons dans la mémoire sizeof (char) * ii * cmncnt_elemsize, ce que nous souhaitons puisqu'un caractère est un octet. Votre code a essayé de faire la même chose en avançant sizeof (void) * ii * cmncnt_elemsize, mais void n’a pas de taille, le compilateur vous a donc donné l’erreur.

Je changerais T10CNT et T20CNT pour utiliser à la fois int ou long au lieu d'un avec chacun. Vous dépendez de sizeof (int) == sizeof (long)

Sur cette ligne:

CMNCNT *cmncnt = (CMNCNT *)&cmncnt[ii*cmncnt_elmsize];

Vous essayez de déclarer une nouvelle variable appelée cmncnt, mais une variable portant ce nom existe déjà en tant que paramètre de la fonction. Vous voudrez peut-être utiliser un nom de variable différent pour résoudre ce problème.

Vous pouvez également souhaiter passer un pointeur sur une fonction CMNCNT à la place d'un pointeur vide, car le compilateur procédera à l'arithmétique du pointeur sans que vous ayez à le lancer. Je ne vois pas l'intérêt de passer un pointeur vide lorsque tout ce que vous en ferez sera converti en un CMNCNT. (Ce qui n'est d'ailleurs pas un nom très descriptif pour un type de données.)

Votre expression

(CMNCNT *)&cmncntin[ii*cmncnt_elmsize]

tente de prendre l'adresse de cmncntin [ii * cmncnt_elmsize], puis transforme ce pointeur en type (CMNCNT *). Il ne peut pas obtenir l'adresse de cmncntin [ii * cmncnt_elmsize] car cmncntin a le type void *.

Étudiez les priorités de l'opérateur C et insérez des parenthèses si nécessaire.

Point d'information: le rembourrage interne peut vraiment gâcher cela.

Considérons struct {char c [6]; }; - Il a sizeof () = 6. Mais si vous en avez un tableau, chaque élément peut être complété avec un alignement de 8 octets!

Certaines opérations d’assemblage ne traitent pas correctement les données mal alignées. (Par exemple, si un int s'étend sur deux mots de mémoire.) (OUI, j'ai déjà été mordu par cela auparavant.)

.

Deuxièmement: dans le passé, j’utilisais des tableaux de tailles variables. (J'étais idiot à l'époque ...) Cela fonctionne si vous ne changez pas de type. (Ou si vous avez une union des types.)

Exemple:

struct T { int sizeOfArray;  int data[1]; };

Attribué comme

T * t = (T *) malloc( sizeof(T) + sizeof(int)*(NUMBER-1) );
                      t->sizeOfArray = NUMBER;

(Bien que le rembourrage / alignement puisse encore vous gâcher.)

.

Troisième: prenez en compte:

   struct T {
     int sizeOfArray;
     enum FOO arrayType;
     union U { short s; int i; long l; float f; double d; } data [1];
    };

Cela résout les problèmes de savoir comment imprimer les données.

.

Quatrième: vous pouvez simplement passer le tableau int / long à votre fonction plutôt qu'à la structure. E.g:

void printCommonStatistics( int * data, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << data[i] << endl;
}

Invoqué via:

_T10CNT  foo;
printCommonStatistics( foo._cnt, 20 );

Ou:

 int a[10], b[20], c[30];
printCommonStatistics( a, 10 );
printCommonStatistics( b, 20 );
printCommonStatistics( c, 30 );

Cela fonctionne beaucoup mieux que de cacher des données dans des structures. Lorsque vous ajoutez des membres à l'une de vos structures, la disposition peut changer entre vos structures et ne plus être cohérente. (Cela signifie que l'adresse de _cnt par rapport au début de la structure peut changer pour _T10CNT et non pour _T20CNT. Les temps de débogage amusants ici. Une structure unique avec une charge utile _cnt 'unifiée éviterait cela.)

Exemple:

struct FOO {
  union {
         int     bar  [10];
          long biff [20];
   } u;
}

.

Cinquième: Si vous devez utiliser des structures ... C ++, iostream et la création de modèles seraient beaucoup plus simples à mettre en œuvre.

Exemple:

template<class TYPE> void printCommonStatistics( TYPE & mystruct, int count )
{
  for( int i=0;  i<count;  i++ )
    cout << "FOO: " << mystruct._cnt[i] << endl;
}      /* Assumes all mystruct's have a "_cnt" member. */

Mais ce n'est probablement pas ce que vous cherchez ...

C n’est pas ma tasse de café, mais je pense que votre problème est que "void * cmncnt". devrait être CMNCNT * cmncnt.

N'hésitez pas à me corriger maintenant, programmeurs C, et dites-moi que c'est pourquoi les programmeurs java ne peuvent pas avoir de belles choses.

Cette ligne est un peu torturée, vous ne pensez pas?

CMNCNT *cmncnt = (CMNCNT *)&cmncntin[ii*cmncnt_elmsize];

Que diriez-vous de quelque chose de plus semblable à

CMNCNT *cmncnt = ((CMNCNT *)(cmncntin + (ii * cmncnt_elmsize));

Ou mieux encore, si cmncnt_elmsize = sizeof (CMNCNT)

CMNCNT *cmncnt = ((CMNCNT *)cmncntin) + ii;

Cela devrait également vous débarrasser de l'avertissement, puisque vous ne dressez plus un vide *.

BTW: Je ne sais pas vraiment pourquoi vous le faites de cette façon, mais si cmncnt_elmsize n'est parfois pas sizeof (CMNCNT), et peut en fait varier d'un appel à l'autre, je suggérerais de repenser cette conception. Je suppose qu’il pourrait y avoir une bonne raison à cela, mais cela me semble très fragile. Je peux presque garantir qu'il existe un meilleur moyen de concevoir des choses.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top