45 votes

+ = opérateur pour uint16_t promeut la valeur assignée à int et ne compilera pas

C'est un vrai WTF pour moi, ressemble à un bug de GCC, mais j'aimerais avoir la communauté ont un coup d'oeil et de trouver une solution pour moi.

Voici le programme le plus simple que j'ai pu rassembler:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
 uint16_t i = 1;
 uint16_t j = 2;
 i += j;
 return i;
}

Je suis en train de compiler ce sur GCC avec -Werror=conversion drapeau, que j'utilise pour une grande partie de mon code.

Voici le résultat:

.code.tio.c: In function ‘main':
.code.tio.c:9:7: error: conversion to ‘uint16_t {aka short unsigned int}' from ‘int' may alter its value [-Werror=conversion]
  i += j;

Même erreur serait de se passer de ce code:

uint16_t i = 1;
i += ((uint16_t)3);

L'erreur est

.code.tio.c: In function ‘main':
.code.tio.c:7:7: error: conversion to ‘uint16_t {aka short unsigned int}' from ‘int' may alter its value [-Werror=conversion]
  i += ((uint16_t)3);
       ^

Juste pour être clair, l'erreur ici est sur l' += opérateur, de ne PAS le casting.

Il ressemble à la surcharge d'opérateur pour l' += avec uint16_t est foiré. Ou ai-je raté quelque chose de subtil ici?

Pour votre utilisation: MCVE

Edit: un peu plus de la même chose:

.code.tio.c:8:6: error: conversion to ‘uint16_t {aka short unsigned int}' from ‘int' may alter its value [-Werror=conversion]
  i = i + ((uint16_t)3);

Mais i = (uint16_t)(i +3); au moins des œuvres...

33voto

dbush Points 8590

La raison de la conversion implicite est due à la reconnaissance de l'équivalence de l' += opérateur = et +.

À partir de la section 6.5.16.2 de la norme C:

3 Un composé d'affectation de la forme E1 op= E2 est équivalent à la simple expression d'affectation E1 = E1 op E2), sauf que la lvalue E1 est évaluée qu'une seule fois, et à l'égard d'un pour une période indéterminée-séquencé appel de la fonction, le fonctionnement d'un composé mission d'évaluation unique

Donc ceci:

i += ((uint16_t)3);

Est équivalent à:

i = i + ((uint16_t)3);

Dans cette expression, les opérandes de l' + de l'opérateur sont promues int, et qu' int est affectée à un uint16_t.

Section 6.3.1.1 détails la raison de ceci:

2 Les personnes suivantes peuvent être utilisées dans une expression où l' int ou unsigned int peut être utilisé:

  • Un objet ou d'une expression avec un type entier (autres que int ou unsigned int) dont la conversion d'entier rang est inférieur ou égal à le rang de l' int et unsigned int.
  • Un peu de champ de type _Bool, int, signed intou unsigned int.

Si un int peut représenter toutes les valeurs du type d'original (que la restriction de l' par la largeur, pour un peu de champ), la valeur est convertie en int; sinon, il est converti en unsigned int. On les appelle les entier promotions. Tous les autres types sont inchangées par l'entier des promotions.

Parce qu'un uint16_t (un.k.un. un unsigned short int) a rang inférieur à celui int, les valeurs sont promus lorsqu'il est utilisé comme opérandes +.

Vous pouvez contourner ce problème en divisant l' += de l'opérateur et le casting du côté droit. Aussi, en raison de la promotion, de la distribution sur la valeur 3 n'a pas d'effet, de sorte que peut être retiré:

i =  (uint16_t)(i + 3);

A noter toutefois que cette opération est soumise à débordement, qui est l'une des raisons pour lesquelles un avertissement est donné quand il n'y a pas de cast. Par exemple, si i a la valeur 65535, alors i + 3 type int et la valeur 65538. Lorsque le résultat est jeté en arrière pour uint16_t, la valeur 65536 est soustraite à partir de cette valeur de rendement de la valeur 2, qui est ensuite affecté à l' i.

Ce comportement est bien défini dans ce cas parce que le type de destination n'est pas signé. Si le type de destination ont été signés, le résultat serait à la mise en œuvre définies.

11voto

Lundin Points 21616
i += ((uint16_t)3);

est égal à(1)

i = i + ((uint16_t)3);

Le plus à droite opérande est explicitement converti à partir d' int (le type de la constante entière 3) à l' uint16_t par la fonte. Après cela, l'habitude de l'arithmétique des conversions(2) sont appliquées sur les deux opérandes de l' +, après quoi les deux opérandes sont implicitement converti en int. Le résultat de l' + de l'opération est de type int.

Puis, vous essayez de stocker une int en uint16_t qui correctement un avertissement à partir d' -Wconversion.

Une possible solution de contournement si vous souhaitez éviter d'attribuer un int d'un uint16_t serait quelque chose comme ceci (MISRA-C conforme etc):

i = (uint16_t)(i + 3u);

(1) C'est obligatoire pour l'ensemble composé des opérateurs d'affectation, C11 6.5.16.2:

Un composé de l'affectation de la forme E1 op= E2 est l'équivalent de la simple expression d'affectation E1 = E1 op (E2), sauf que la lvalue E1 est évaluée qu'une seule fois,

(2) Voir Implicite des règles de promotion de type pour plus d'infos sur l'implicite du type de promotions.

3voto

ikegami Points 133140

Une explication se trouve ici:

joseph[at]codesourcery.com 2009-07-15 14:15:38 UTC
Sujet: Re: -Wconversion: ne pas avertir des opérandes n'est pas plus grand que le type de cible

Le Wed, 15 Jul 2009, ian airs dot com a écrit:

> Bien sûr, il peut envelopper, mais -Wconversion n'est pas pour l'emballage des avertissements.

C'est pour les avertissements sur les conversions implicites de la modification d'une valeur; la l'arithmétique, dans une plus large type (volontairement ou non), ne pas envelopper, mais la valeur est modifiée par la conversion implicite de retour à l'omble chevalier. Si l'utilisateur avait conversions explicites de type int dans leur moyenne arithmétique, il n'y aurait pas de doute que la mise en garde est appropriée.

L'avertissement se produit parce que le compilateur a l'ordinateur effectue l'arithmétique à l'aide d'un plus grand type de uint16_t (un int, entier par le biais de la promotion), et de placer la valeur de retour dans une uint16_t pourrait le tronquer. Par exemple,

uint16_t i = 0xFFFF;
i += (uint16_t)3;     /* Truncated as per the warning */

Le même s'applique à la cession distincte et plus les opérateurs.

uint16_t i = 0xFFFF;
i = i + (uint16_t)3;  /* Truncated as per the warning */

2voto

supercat Points 25534

Il existe de nombreux cas où il serait utile d'effectuer des opérations sur entiers directement sur petit entier non signé types. Parce que le comportement de l' ushort1 = ushort1+intVal; sera dans tous les cas définis être équivalent à contraindre intVal pour le type d' ushort1 et en effectuant ensuite le plus directement de ce type, cependant, les auteurs de la Norme ne voyait pas la nécessité d'écrire des règles spéciales pour cette situation. Je pense qu'ils ont clairement reconnu qu'un tel comportement a été utile, mais qu'ils attendaient que les implémentations devraient généralement se comporter de façon à savoir si ou non le modèle de mandat il.

D'ailleurs, gcc parfois procédés arithmétiques sur des valeurs de type uint16_t de façons différentes lorsque le résultat est contraint à l' uint16_t que dans les cas où il ne l'est pas. Par exemple, étant donné

uint32_t multest1(uint16_t x, uint16_t y)
{
  x*=y;
  return x;
}

uint32_t multest2(uint16_t x, uint16_t y)
{
  return (x*y) & 65535u;
}

La fonction multest1() semble constamment effectuer la multiplication mod 65536 dans tous les cas, mais la fonction multest2 ne le fait pas. Par exemple, la fonction:

void tester(uint16_t n, uint16_t *p)
{
  n|=0x8000;
  for (uint16_t i=0x8000; i<n; i++)
    *p++= multest2(65535,i);
  return 0;
}

sera optimisé équivalent à:

void tester(uint16_t n, uint16_t *p)
{
  n|=0x8000;
  if (n != 0x8000)
    *p++= 0x8000;
  return 0;
}

mais cette simplification ne pas se produire lors de l'utilisation d' multest1. Je ne serais pas envisager de gcc mod-65536 comportement comme fiables, mais la différence de génération de code montre que:

  1. Certains compilateurs, y compris gcc, effectuez mod 65536 arithmétique directement lorsqu'un résultat va être convertie directement à uint16_t, mais...

  2. Certains compilateurs processus de dépassement d'entier, ce qui pourrait provoquer des dysfonctionnements comportement, même si le code ignore complètement tous les bits de poids de la raison, de sorte qu'un compilateur qui essaye de les avertir de tous les possibles UB doit drapeau des constructions compilateurs serait autorisé à traiter en bête de mode que la portabilité des violations.

Bien qu'il existe de nombreuses déclarations de la forme ushort1+=intval qui ne pouvait pas entraîner un dépassement de capacité, il est plus facile de affichez à toutes ces déclarations que seulement identifier ceux qui pourraient en réalité entraîner des erronée comportement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X