comportement étrange de scanf pour short int

Le code est comme suit:

#include  main() { int m=123; int n = 1234; short int a; a=~0; if((a>>5)!=a){ printf("Logical Shift\n"); m=0; } else{ printf("Arithmetic Shift\n"); m=1; } scanf("%d",&a); printf("%d\n", m); } 

après la ligne scanf("%d",&a); la valeur de m devient 0 .

Je sais que cela peut être causé par le scanf: le type de a est court et le type de l’entrée est int. Mais comment cela peut-il affecter la valeur de m?

Merci beaucoup !

La raison la plus probable pour que m soit 0 dans votre extrait de code est parce que vous atsortingbuez à m cette valeur dans le corps de votre instruction if, mais comme le code contient un comportement indéfini, personne ne peut le dire avec certitude.


L’histoire probable de passer un short* quand scanf s’attend à un int*

En supposant que sizeof(short) = 2 et sizeof(int) == 4 .

Lorsque vous entrez dans votre fonction principale, la stack sur laquelle résident les variables devrait normalement ressembler à ce qui suit:

  _ |short int (a) : scanf will try to read an int (4 bytes). |_ 2 bytes : This part of memory will most |int (n) : likely be overwritten | :.. | |_ 4 bytes |int (m) | | |_ 4 bytes 

Lorsque vous lisez un %d (c’est-à-dire un int ) dans la variable a cela ne devrait pas affecter la variable m , même si n aura probablement des parties écrasées.


Comportement non défini

Bien que ce soit tout un jeu de devinettes puisque vous invoquez ce que nous appelons normalement un ” comportement non défini ” lorsque vous utilisez votre instruction scanf.

Tout ce que la norme ne garantit pas est UB, et le résultat pourrait être n’importe quoi. Vous pourrez peut-être écrire des données sur un autre segment faisant partie d’une variable différente, ou peut-être faire imploser l’univers.

Personne ne peut garantir que nous vivrons pour voir un autre jour quand UB sera présente.


Comment lire un short int utilisant scanf

Utilisez %hd , et assurez-vous de passer un short* .. nous en avons assez d’UB pour une nuit!

En supposant que int et short soient des nombres entiers de quatre et deux octets, respectivement, sur votre plate-forme (ce qui est une hypothèse probable, mais non garantie par le standard), vous demandez à scanf de lire un entier et de le stocker dans quatre octets. : les deux octets de b , et ce que deux octets suivent en mémoire. (Eh bien, techniquement, il s’agit d’un comportement indéfini, et aucun comportement spécifique n’est garanti, mais c’est ce qu’il est susceptible de faire.) Apparemment, votre compilateur utilise les deux octets après b comme les deux premiers octets de m . Ce qui est un peu surprenant – je ne m’attendrais certainement pas à ce que b et m soient adjacents, et cela implique plutôt que votre compilateur n’aligne pas les points abrégés et les int ent au début des blocs de quatre octets – mais parfaitement légal.

Vous pouvez voir mieux ce qui se passe si vous ajoutez

 printf("&a: %08X\n&m: %08X\n", (int)&a, (int)&m); 

qui vous montrera où a et m sont stockés les uns par rapport aux autres. (Juste comme test, je veux dire. Vous ne voudriez pas que cela soit dans du “vrai” code.)

Vous avez raison, %d attend et écrit un int . Si vous entrez une valeur inférieure à 65535 , les octets sont insérés en dehors de short , ainsi 0 apparaît lorsque vous imprimez a retour. J’ai essayé de lire un short et de le réimprimer; Je suis entré dans 65536123 et j’ai 123 , ce qui est parfaitement logique (65536 occupe précisément 16 bits; vous voyez les 123 restants à travers les deux octets du shortshort ). Ce comportement est dangereux, car les deux autres octets du short terme se retrouvent dans une “variable voisine” du short , ce qui est très, très mauvais. J’espère que cela devrait vous convaincre de ne pas le faire.

PS Pour lire un short avec scanf , déclarez une variable int temporaire, lisez-y la valeur à l’aide de scanf , puis transmettez-la à short .

Vous appelez Undefined Behavior lorsque vous passez un pointeur sur un non-int à% d de scanf.

Probablement, le compilateur introduit des octets de remplissage à des fins d’alignement et les valeurs sont stockées dans les octets de remplissage et non dans les octets “utiles”.

Cependant, le compilateur est libre de déclencher une violation de segfault / d’access pour invoquer des démons nasaux.

Si vous aviez réellement utilisé la variable n , elle aurait probablement été celle qui a été détruite, plutôt que m . Puisque vous n’avez pas utilisé n , le compilateur l’a optimisé, ce qui signifie que c’est m qui a été bouleversé par scanf() écrit 4 octets (car on lui a dit qu’il avait un pointeur sur un entier (4 octets)). au lieu des 2 octets. Cela dépend de nombreux détails de votre matériel, tels que la finalité et l’alignement (si int devait être aligné sur une limite de 4 octets, vous ne verriez pas le problème; je suppose que vous êtes plutôt sur une machine Intel que, par exemple, PowerPC ou SPARC).

Ne blessez pas votre compilateur – même accidentellement. Il aura son propre dos.