1
votes

Changement de tableau flexible en pointeur

Je travaille pour me débarrasser des violations MISRA présentes dans mon code C. Elle enfreint la règle 18.7 .

struct abc {
  struct header;
  uint8_t *data;
};

Ici, la ligne 1 est à l'origine des violations MISRA.

J'ai essayé de la convertir en: p>

struct abc {
  struct header;
  uint8_t data[]; /* Line 1 */
};

Pouvons-nous faire comme ci-dessus ou est-ce que cela enfreint quelque chose?


5 commentaires

Puisque les règles MISRA ne sont pas FoC, il serait utile que vous nous disiez ce qu'est la règle 18.7.


Règle 18.7 Les membres du tableau flexible ne doivent pas être déclarés.


Les membres de tableau flexibles sont problématiques car le compilateur ne peut pas connaître leurs tailles réelles, mais il peut les traiter comme s'il le faisait, auquel cas il utilisera la taille de la partie de base de la structure, à l'exclusion du membre de tableau flexible. Cela peut provoquer des bugs, car la mauvaise taille est utilisée. Les solutions proposées pour utiliser à la place data [0] ou data [1] ne résolvent pas ce problème. Si vous connaissez la taille maximale des données et qu'elle est raisonnable (pas trop d'espace gaspillé quand tout n'est pas utilisé), alors déclarer le tableau avec cette taille pourrait être une solution.


Le problème ici n'est pas de «se débarrasser des violations de la MISRA». Le problème réel est «Accomplissez une tâche sans violations de la MISRA». La bonne solution dépend de la tâche. À quoi cette structure était-elle censée être utilisée? Est-il utilisé pour traiter ou gérer des données existantes, et vous devez l'adapter aux données? Ou concevez-vous un nouvel algorithme et pouvez-vous utiliser n'importe quelle structure de données appropriée? Quelle est la tâche réelle à effectuer?


@EricPostpischil - le problème est "d'accomplir une tâche sans violations non résolues " - une violation peut être acceptée telle quelle (règles consultatives) ou sujette à une déviation (règles obligatoires) - ou modifier le code source


3 Réponses :


0
votes

Oui, vous pouvez, car cela rend la taille de la structure déterministe et statique, mais cela vous oblige également à allouer puis à libérer l'espace nécessaire pour les données avec malloc () et free () , ou faites-le pointer explicitement vers un espace déjà disponible quelque part, chaque fois que vous instanciez la structure.

Ce que vous voulez probablement faire ici est de spécifier une longueur définie pour votre tableau. Si toutefois cette structure est destinée à décrire réellement l'en-tête d'un bloc de données, vous pouvez utiliser data [1] puis laisser votre index dépasser cette valeur pour accéder au reste (ISO C interdit les tableaux de longueur 0, bien que).


4 commentaires

Bien que l'utilisation de data [1] puisse satisfaire le libellé de MISRA et du vérificateur automatisé, cela ne satisferait pas l'intention - ce serait quand même une structure dont l'utilisation réelle avec des données de taille variable différait de sa déclaration comme une structure de taille fixe, l'exposant aux mêmes problèmes qu'un membre de tableau flexible. Une solution serait de déclarer le tableau avec la taille maximale à gérer, si elle est connue et raisonnable. Sinon, une autre solution devrait être imaginée.


C'est ce que je voulais dire en disant " Ce que vous voulez probablement faire ici est de spécifier une longueur définie pour votre tableau ." J'aurais peut-être dû développer un peu ...


Vous n'êtes pas "obligé" d'utiliser malloc dans ce cas. C'est une possibilité, mais le pointeur peut faire référence à n'importe quel objet mémoire. Les implémentations de malloc par défaut sont généralement non déterministes. Un allocateur de mémoire de bloc fixe peut être utilisé, ou même simplement un pointeur vers un objet dont la propriété et la gestion sont indiquées dans la structure d'en-tête.


Encore une fois, c'est ce que je voulais dire par "_ ou faire explicitement référence à un espace déjà disponible quelque part_". Et je crois que nous sommes tous d'accord là-dessus aussi: cela ferait plaisir à MISRA, mais au prix de quelque chose qui est probablement encore plus dangereux (pointeurs non alloués, fuites de mémoire et segfaults).



2
votes

Votre solution est sémantiquement différente et ne fonctionnera pas même si elle efface la violation.

L'intention ici est de créer une structure qui peut servir d'en-tête pour les données contiguës qui la suivent. Par exemple, si vous avez:

struct abc 
{
  struct header;
  uint8_t data[1] ;
} ;

De telle sorte que message.info.data et message.data font référence au même chose et transtyper une struct abc en un struct Message permet de définir une fonction pour passer n'importe quel objet avec un en-tête struct abc . Soutenir efficacement le polymorphisme en C.

Le remplacer par:

struct abc 
{
  struct header;
  uint8_t data[0] ;
} ;

est sémantiquement différent car le membre data ne fait pas référence à les données contiguës avec en-tête . La sémantique de la copie diffère également, et il est peu probable dans le contexte du code qui utilise la structure d'origine que cela fonctionnera comme prévu.

GCC prend en charge la syntaxe suivante:

struct abc 
{
  struct header;
  uint8_t* data;
};


4 commentaires

Bien que l'utilisation de data [0] ou data [1] puisse satisfaire le libellé de MISRA et du vérificateur automatisé, cela ne satisferait pas l'intention - ce serait quand même une structure dont l'utilisation réelle avec des données de taille variable différait de sa déclaration en tant que structure de taille fixe, l'exposant aux mêmes problèmes qu'un membre de tableau flexible. Une solution serait de déclarer le tableau avec la taille maximale à traiter, si elle est connue et raisonnable. Sinon, une autre solution devrait être imaginée.


@EricPostpischil: oui, tout ça. J'étais plus préoccupé ici que la sémantique ne devrait pas être changée en changeant naïvement la structure. Se conformer à l'esprit plutôt qu'à la règle nécessitera probablement une refonte plus importante affectant le code plus large. L'utilisation d'un pointeur nécessiterait l'allocation de mémoire à partir d'un pool ou d'un tas et la gestion conséquente de la propriété, de la durée de vie et de la libération.


data [0] n'est pas valide ISO C donc il enfreint MISRA-C. data [1] est le "struct hack" et le comportement indéfini, donc il viole également MISRA-C. La solution correcte consiste à déclarer un tableau de taille fixe.


@Lundin Tout est vrai. Les données [1] elles-mêmes ne sont pas non conformes; l'utiliser comme un "struct hack" pourrait bien l'être. Mon intention n'était pas de fournir une solution conforme, mais de souligner les différences sémantiques entre elles. Le problème ne concerne pas vraiment ce membre unique de la structure, car toute solution conforme nécessiterait des modifications de conception et de mise en œuvre ailleurs. Le code original dit sémantiquement «les données suivent», tandis que le membre du pointeur dit «les données sont ailleurs», donc l'emplacement, la validité et la cohérence de ces données doivent ensuite être traités.



1
votes

Tous les systèmes liés à la sécurité interdisent l'allocation dynamique de mémoire, c'est pourquoi MISRA-C: 2012 le fait également. C'est la raison d'être de la règle 18.7: les membres de tableau flexible sont étroitement associés à l'allocation dynamique et ne sont donc pas autorisés.

La raison pour laquelle l'allocation dynamique est interdite est qu'il ne peut y avoir de comportement non déterministe dans ce type de système. De plus, cela n'a aucun sens pour utiliser l'allocation dynamique dans les applications de microcontrôleur / RTOS.

Vous pouvez remplacer le membre du tableau flexible par un pointeur si cela a du sens pour votre application. Mais s'il s'agit d'une sorte d'en-tête de protocole ou de structure de données, vous voudrez probablement plutôt un tableau de taille fixe. (Et le remplissage de la structure de l'esprit: le stockage des protocoles de communication de données dans les structures peut être problématique en raison de l'alignement et de la finalité.)


0 commentaires