Un avertissement - comparaison entre des expressions entières signées et non signées

StackOverflow https://stackoverflow.com/questions/3660901

  •  01-10-2019
  •  | 
  •  

Question

Je travaille actuellement à Accelerated C ++ et ont rencontré un problème dans l'exercice 2-3.

Un aperçu rapide du programme - le programme prend essentiellement un nom, puis affiche un message d'accueil dans un cadre d'astérisques - à savoir Bonjour! entouré encadrée par * 's.

L'exercice - Dans l'exemple de programme, les auteurs utilisent const int pour déterminer le rembourrage (espaces vides) entre le message d'accueil et les astérisques. Ils demandent alors le lecteur, dans le cadre de l'exercice, pour demander à l'utilisateur pour l'entrée à la taille, ils veulent que le rembourrage soit.

Tout cela semble assez facile, je vais demander à l'avance l'utilisateur pour deux entiers (int) et de les stocker et modifier le programme à utiliser ces entiers, en supprimant ceux utilisés par l'auteur, lors de la compilation si je reçois l'avertissement suivant;

  

Exercise2-3.cpp: 46: avertissement: comparaison entre des expressions entières signée et non signée

Après quelques recherches, il semble que les tentatives de code pour comparer l'un des entiers ci-dessus (int) à un string::size_type, ce qui est bien. Mais je me demandais - ce que cela signifie que je devrais changer l'un des entiers à unsigned int? Est-il important de préciser explicitement si mes entiers sont signés ou non signés?

 cout << "Please enter the size of the frame between top and bottom you would like ";
 int padtopbottom;
 cin >> padtopbottom;

 cout << "Please enter size of the frame from each side you would like: ";
 unsigned int padsides; 
 cin >> padsides;

 string::size_type c = 0; // definition of c in the program
 if (r == padtopbottom + 1 && c == padsides + 1) { // where the error occurs

Ci-dessus sont les bits correspondants du code, le c est de type string::size_type parce que nous ne savons pas combien de temps le message d'accueil peut-être - mais pourquoi dois-je obtenir ce problème maintenant, quand le code de l'auteur n'a pas eu le problème lors de l'utilisation const int? De plus - à tous ceux qui peuvent avoir terminé Accelerated C ++ - ce que cela sera expliqué plus loin dans le livre

Je suis sur Linux Mint en utilisant g ++ via Geany, si cela aide ou fait une différence (comme je l'ai lu qu'il pouvait pour déterminer ce qui est string::size_type).

Était-ce utile?

La solution

Il est généralement une bonne idée de variables déclarent comme unsigned ou size_t si elles seront comparées aux tailles, pour éviter ce problème. Autant que possible, utilisez le type exact que vous compareront contre (par exemple, l'utilisation std::string::size_type lorsque l'on compare avec la longueur d'un std::string).

Compilateurs donnent des avertissements sur la comparaison de types signés et non signés parce que les gammes de ints signés et non signés sont différents, et quand ils sont comparés les uns aux autres, les résultats peuvent être surprenants. Si vous devez faire une comparaison d'un tel, vous devez convertir explicitement l'une des valeurs à un type compatible avec l'autre, peut-être après vérification pour assurer que la conversion est valide. Par exemple:

unsigned u = GetSomeUnsignedValue();
int i = GetSomeSignedValue();

if (i >= 0)
{
    // i is nonnegative, so it is safe to cast to unsigned value
    if ((unsigned)i >= u)
        iIsGreaterThanOrEqualToU();
    else
        iIsLessThanU();
}
else
{
    iIsNegative();
}

Autres conseils

J'ai eu le problème par 2-3 travail hier problème exactement le même dans Accelerated C ++. La clé est de changer toutes les variables que vous compareront (à l'aide des opérateurs booléens) à des types compatibles. Dans ce cas, cela signifie que string::size_type (ou unsigned int, mais étant donné que cet exemple utilise le premier, je vais simplement rester avec que même si les deux sont techniquement compatibles).

Notez que dans leur code d'origine, ils ont fait exactement ce pour le compteur c (page 30 à la section 2.5 du livre), comme vous l'avez justement souligné.

Ce qui rend cet exemple plus compliqué est que les différentes variables de remplissage (padsides et padtopbottom), ainsi que tous les compteurs, doit aussi être modifiés à string::size_type.

Comment arriver à votre exemple, le code qui vous finirait au courant ressembler à ceci:

cout << "Please enter the size of the frame between top and bottom";
string::size_type padtopbottom;
cin >> padtopbottom;

cout << "Please enter size of the frame from each side you would like: ";
string::size_type padsides; 
cin >> padsides;

string::size_type c = 0; // definition of c in the program

if (r == padtopbottom + 1 && c == padsides + 1) { // where the error no longer occurs

Notez que dans la condition précédente, vous obtiendrez l'erreur si vous ne l'avez pas initialisez r variable en string::size_type dans la boucle de for. Vous devez donc initialiser le pour utiliser quelque chose comme la boucle:

    for (string::size_type r=0; r!=rows; ++r)   //If r and rows are string::size_type, no error!

Donc, en gros, une fois que vous introduisez une variable string::size_type dans le mélange, chaque fois que vous voulez effectuer une opération booléenne sur ce point, tous les opérandes doivent avoir un type compatible pour le compiler sans avertissement préalable.

La différence importante entre ints signés et non signés est l'interprétation du dernier bit. Le dernier bit dans les types signés représentent le signe du nombre, ce qui signifie: par exemple:

0001 est de 1 signés et non signés 1001 est -1 signée et non signée 9

(j'évité toute la question du complément pour la clarté de l'explication! Ce n'est pas exactement comment ints sont représentés dans la mémoire!)

Vous pouvez imaginer que cela fait une différence de savoir si vous comparez avec ou -1 à 9. Dans de nombreux cas, les programmeurs sont tout simplement trop paresseux déclarer ints de comptage comme non signé (ballonnements la tête de la boucle f.i.) Il est généralement pas un problème car avec ints vous devez compter 2 ^ 31 jusqu'à ce que votre bit de signe que vous mord. Voilà pourquoi il est seulement un avertissement. Parce que nous sommes trop paresseux pour écrire « non signé » au lieu de « int ».

Dans les plages de extrêmes, un entier non signé peut devenir plus grand qu'un int.
Par conséquent, le compilateur génère un avertissement. Si vous êtes sûr que ce n'est pas un problème, ne hésitez pas à jeter les types au même type de sorte que le disparait d'avertissement (utilisation C ++ fonte de sorte qu'ils sont faciles à repérer).

Vous pouvez également faire les variables du même type pour arrêter le compilateur de se plaindre.
Je veux dire, est-il possible d'avoir un rembourrage négatif? Si oui, alors le garder comme un entier. Sinon, vous devriez probablement utiliser unsigned int et laisser les prises de courant les situations où les types d'utilisateur dans un numéro de négatif.

ou utilisez cette bibliothèque d'en-tête et écriture:

// |notEqaul|less|lessEqual|greater|greaterEqual
if(sweet::equal(valueA,valueB))

et ne se soucient pas de tailles signés / non signés ou différents

Le principal problème est que le matériel sous-jacent, la CPU, ne dispose que des instructions pour comparer deux valeurs signées ou comparer deux valeurs non signées. Si vous passez l'instruction de comparaison non signé a signé, la valeur négative, elle sera traitée comme un grand nombre positif. Ainsi, -1, la configuration binaire avec tous les bits sur (complément à deux), devient la valeur maximale non signé pour le même nombre de bits.

8 bits: -1 signé est que les mêmes bits non signé 255 16 bits signés: -1 sont les mêmes bits que 65.535 unsigned etc.

Donc, si vous avez le code suivant:

int fd;
fd = open( .... );

int cnt;
SomeType buf;

cnt = read( fd, &buf, sizeof(buf) );

if( cnt < sizeof(buf) ) {
    perror("read error");
}

vous constaterez que si la lecture (2) appel échoue en raison du descripteur de fichier non valide devenir (ou une autre erreur), qui cnt sera réglé sur -1. Lorsque l'on compare à sizeof (BUF), une valeur non signée, l'instruction if () sera faux parce 0xffffffff est pas moins sizeof () certains (raisonnable, non concoctée pour être la taille maximale) structure de données.

Ainsi, vous devez écrire ci-dessus si, pour supprimer la signature / avertissement non signé comme:

if( cnt < 0 || (size_t)cnt < sizeof(buf) ) {
    perror("read error");
}

parle haut et fort juste aux problèmes.

1.  Introduction of size_t and other datatypes was crafted to mostly work, 
    not engineered, with language changes, to be explicitly robust and 
    fool proof.
2.  Overall, C/C++ data types should just be signed, as Java correctly
    implemented.

Si vous avez des valeurs si grand que vous ne pouvez pas trouver un type de valeur signée qui fonctionne, vous utilisez trop petit d'un processeur ou trop grand d'une grandeur de valeurs dans la langue de votre choix. Si, comme avec de l'argent, tous les comptes de chiffres, il existe des systèmes à utiliser dans la plupart des langues qui vous fournissent des chiffres infinis de précision. C / C ++ ne fonctionne tout simplement pas bien cela, et vous devez être très explicite sur tout autour de types comme mentionné dans la plupart des autres réponses ici.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top