4
votes

Comment affecter une variable de champ de bits à la variable uint8_t sans violer les règles MISRA?

J'ai une typedef struct nommée Character.

typedef struct {
    unsigned int a : 1;
    unsigned int b : 1;
    unsigned int c : 1;
    unsigned int d : 1;
    unsigned int o : 1;
    unsigned int p : 1;
    unsigned int q : 1;
    unsigned int x : 1;
} Character;

static Character tempChar;

void writeVar(const uint8_t *pData)
{
    tempChar.a = pData[0] >> 5;
    ...
}

Lorsque j'essaie d'attribuer une variable uin8_t (avec la valeur 0 ou 1 ) à l'un de ces champs de bits, j'ai une violation de la règle MISRA 10.6 qui stipule que:

La valeur d'une expression composite ne doit pas être attribuée à un objet avec un type essentiel plus large

Existe-t-il un moyen d'assigner un champ de bits à uint8_t sans violer MISRA C?


10 commentaires

Comment avez-vous cet avertissement? Comment compilez-vous le code?


J'utilise un outil pour faire l'analyse statique. Je dois me conformer à MISRA C.


Avez-vous essayé tempChar.a = (uint8_t)(pData[0] >> 5); ?


Oui je l'ai fait. Cela n'a pas aidé.


Essayez d'éviter la promotion int : tempChar.a = ((unsigned)pData[0]) >> 5;


Avez-vous essayé de convertir la valeur en unsigned int avant l'affectation ( tempChar.a = (unsigned int)(pData[0] >> 5); )?


@LaurentH. Pardon. Je n'ai pas essayé votre suggestion de la bonne manière. Maintenant, le casting a résolu le problème. Merci


Et si je fais uint8_t t = 0xFF; writeVar(&t); 0xFF >> 5 n'est pas 0 ou 1. Normalement, l'assignation coupe les bits supérieurs, mais peut-être que MISRA a raison de se plaindre? L'erreur disparaît-elle d'elle-même si vous ajoutez explicitement "& 0x01"?


Character ne devrait-il pas utiliser uint8_t? Cela semble inutile d'utiliser 32 bits pour stocker 8.


Qu'est-ce que "MISRA"?


4 Réponses :


0
votes

Je trouve MISRA C trop compliqué pour cette raison même. Quoi qu'il en soit, vous n'avez pas dit que vous vouliez l'attribuer directement. Si tel est le cas, vous pouvez recourir à ce qui suit:

if(1 == tempChar.a)
{ 
    // Some code
}

Et définissez ces valeurs en accédant à tempChar.u8Value au lieu des champs de bits. Par exemple,

tempChar.u8Value |= (1 << 0);

mettrait tempChar.a à 1 .

Cela garderait toujours la netteté (lisibilité) du code dans la même mesure. Par exemple,

typedef union {
    
    struct {
        unsigned int a : 1;
        unsigned int b : 1;
        unsigned int c : 1;
        unsigned int d : 1;
        unsigned int o : 1;
        unsigned int p : 1;
        unsigned int q : 1;
        unsigned int x : 1;
    };
    
    uint8_t u8Value;
    
} Character;


0 commentaires

4
votes

Les deux opérandes de l'expression pData[0] >> 5 seront, si nécessaire, promus en int (cela se produira pour pData[0] ).

Et le résultat de l'expression est un int .

La promotion et la conversion de int en unsigned int , bien que parfaitement valides et correctes dans des cas normaux, suffisent à la très stricte MISRA pour se plaindre.

Une solution simple (comme indiqué dans les commentaires) est de convertir explicitement pData[0] en un unsigned int utilisant la conversion.


2 commentaires

Oh, plus simple. Pourquoi ai-je pensé à l' union en premier lieu! :(


Exécutez le même code avec ~pData[0] >> 5 . Et puis après cela, ~(uint32_t)pData[0] >> 5 . Alors, plaignez-vous de MISRA ... à moins bien sûr que vous ne pensiez que stocker la valeur -1 dans un champ de bits :1 est une bonne idée.



0
votes
tempChar.a = (unsigned int) (pData[0] >> 5U);

0 commentaires

0
votes

Le problème principal ici n'a rien à voir avec MISRA, mais avec la tentative de stocker une valeur à un emplacement spécifique dans un champ de bits. Vous ne pouvez pas savoir comment votre disposition de champ de bits finit réellement en mémoire, car ce n'est pas défini dans la norme C.

Votre champ de bits alloue-t-il 8 bits de valeur dans l'octet MS ou l'octet LS? Prend-il fin en accord ou n'est-ce pas? Quel est l'ordre des bits? Personne ne sait. L'étape 1 consiste à se débarrasser du champ de bits.

L'étape 2 consiste à supprimer tout élément unsigned int et à utiliser uint16_t / uint32_t .


Quant à MISRA-C 10.6 en particulier, la règle interdisant la conversion implicite vers un type plus large a toujours été plutôt erronée. Le raisonnement utilisé par MISRA pour cette règle était d'empêcher les gens d'écrire du code comme uint32_t u32 = u16a + u16b; et en pensant que l'opérande u32 de = signifie en quelque sorte comme par magie que l'opération sera effectuée sur 32 bits au lieu de 16. Mais sur un système 8/16 bits, elle est effectuée avec une arithmétique 16 bits et il peut y avoir des débordements / wrap- environ.

Maintenant, en fait, effectuer des décalages de bits sur les types signés est toujours une très mauvaise idée. pData[0] est implicitement promu en int qui est signé. Il existe d'autres règles MISRA traitant de cela, plutôt que celle que vous avez citée.

Indépendamment de MISRA, vous devez prendre l'habitude de toujours effectuer vos quarts de travail sur les types non signés. «Ce n'est pas dangereux dans ce cas» est une sombre justification. Cela signifie qu'il faut toujours écrire (uint32_t)pData[0] >> 5 et la conversion doit être appliquée avant le décalage et non après. Cela supprime toutes les incertitudes concernant les décalages à gauche de comportement non définis et les décalages à droite potentiellement arithmétiques, etc. Laissez l'optimiseur s'inquiéter de la taille réelle utilisée des opérandes à partir de là.


0 commentaires