Le débordement apparaît même lors de l’utilisation de unsigned long int

Quand je fais le calcul suivant:

unsigned long long int data_size = 60123456 * 128 * sizeof(double); printf("data_size= %llu \n", data_size); 

Je reçois étonnamment un avertissement de débordement:

 test.c:20:49: warning: overflow in expression; result is -894132224 with type 'int' [-Winteger-overflow] unsigned long long int data_size = 60123456 * 128 * sizeof(double); ^ 1 warning generated. 

Je ne peux pas comprendre pourquoi cette erreur apparaît même si j’utilise unsigned long long int ! Quelqu’un peut-il expliquer pourquoi? Je vous remercie

Un débordement se produit avant que la valeur ne soit affectée à la variable. Pour éviter cela, utilisez long suffixe long:

 60123456LL * 128 * sizeof(double) 

(Les commentaires suggèrent également ULL qui n’est pas nécessaire ici car les valeurs sont dans la plage signée, mais ce serait la solution générique)

Vous vous convertissez à “long long” trop tard. 60123456 * 128 est toujours une expression int débordante.

Essayez 60123456LL * 128 place. (LL parce que la valeur résultante n’est pas garantie pour tenir dans un long.)

 unsigned long long int data_size = 60123456 * 128 * sizeof(double); // trouble-some code 

Le type de destination unsigned long long int data_size = n’a pas d’importance pour le calcul du produit 60123456 * 128 * sizeof(double) .

Il est préférable d’assurer que les mathématiques utilisent au moins la taille du type de cible pour éviter tout débordement. Dans le cas de OP, cela implique une constante avec LLU .


Il existe 2 calculs de produit, chacun avec son propre type mathématique.

60123456 est un int ou long fonction de la plage int . Supposons que c’est un int .

60123456 * 128 est un int * int . Le produit mathématique 7695802368 dépasse la plage des entiers signés sur 32 bits, d’où un dépassement d’entier signé ou un comportement non défini (UB) avec un int 32 bits.

Si 60123456 * 128 ne débordait pas, indique 64 bits, la multiplication suivante serait * sizeof(double); et so int * size_t donne un produit de type size_t .

Le calcul du produit doit utiliser au moins unsigned long long calculs unsigned long long comme ci-dessous:

 unsigned long long int data_size = 1LLU * 60123456 * 128 * sizeof(double); // or unsigned long long int data_size = 60123456LLU * 128 * sizeof(double); // or unsigned long long int data_size = 60123456; data_size *= 128 * sizeof(double); // or avoiding naked magic numbers #define A_HEIGHT 60123456 #define A_WIDTH 128 unsigned long long int data_size = 1LLU * A_HEIGHT * A_WIDTH * sizeof(double); 

sizeof (double) indique que le code tente de trouver la taille d’une structure de type 2D. Je m’attendrais à un code comme celui-ci. Notez que le type de résultat de sizeof est size_t . Le calcul du produit s’effectue donc avec au moins size_t math.

 size_t data_size = sizeof(double) * 60123456 * 128; printf("data_size= %zu\n", data_size); 

Voir aussi Pourquoi écrire 1.000.000.000 en 1000 * 1000 * 1000 en C? et ma réponse justifie de ne pas utiliser 1000 * 1000 * 1000 pour les détails applicables.

Les constantes 60123456 et 128 étant de type int , l’expression 60123456 * 128 est également de type int . La valeur de cette expression dépasserait la plage d’un int . Le compilateur est capable de le détecter dans ce cas car les deux opérandes sont des constantes.

Vous devez utiliser le suffixe ULL sur le premier opérande pour rendre le type de la constante unsigned long long . De cette façon, il correspond au type affecté. Ensuite, pour toute opération impliquant cette valeur, l’autre opérande sera promu unsigned long long avant que l’opération ne soit appliquée et vous n’aurez aucun débordement.

Donc, l’expression résultante devrait ressembler à ceci:

 unsigned long long int data_size = 60123456ULL * 128 * sizeof(double); 

Le lexer attache les types sur les constantes pendant la phase de traduction 7 (conversion des jetons de prétraitement en jetons C). Cf ISO9899 5.1.1.2 Translation phases Phases de 5.1.1.2 Translation phases . Comme les autres l’ont dit, le lexer associera à vos constantes le type int au lieu de unsigned long long si vous le souhaitez, et afin que les default arithmetic conversions génèrent une constante de type unsigned long long sur le côté droit de l’affectation, dire au lexer d’attacher le type unsigned long long sur au moins une constante de l’opération * qui génère le débordement, donc en écrivant 60123456ULL * 128 ou 60123456 * 128ULL ou les deux.