web-dev-qa-db-fra.com

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

Je travaille actuellement à travers C++ accéléré et j'ai rencontré un problème dans l'exercice 2-3.

Aperçu rapide du programme - le programme prend un nom, puis affiche un message d'accueil dans un cadre d'astérisques - c'est-à-dire Bonjour! entouré encadré par *.

L'exercice - Dans l'exemple de programme, les auteurs utilisent const int pour déterminer le remplissage (espaces vides) entre le message d'accueil et les astérisques. Ils demandent ensuite au lecteur, dans le cadre de l'exercice, de demander à l'utilisateur quelle est la taille souhaitée du rembourrage.

Tout cela semble assez facile, je demande à l'utilisateur deux entiers (int), puis les stocke et modifie le programme pour qu'il utilise ces entiers, en supprimant ceux utilisés par l'auteur, lors de la compilation, bien que j'obtienne ce qui suit Attention;

Exercice2-3.cpp: 46: avertissement: comparaison entre les expressions entières signées et non signées

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

 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, les bits de code pertinents, le c est de type string::size_type _ parce que nous ne savons pas combien de temps le message d'accueil pourrait être - mais pourquoi ai-je ce problème maintenant, alors que le code de l'auteur ne l'a pas obtenu lors de l'utilisation de const int? En outre, à tous ceux qui peuvent avoir complété C++ accéléré - cela sera expliqué plus tard 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, cela pourrait être le cas pour déterminer quel string::size_type est).

62
Tim Harrington

Il est généralement préférable de déclarer les variables sous la forme unsigned ou size_t Si elles sont comparées à des tailles afin d'éviter ce problème. Dans la mesure du possible, utilisez le type exact avec lequel vous comparerez (par exemple, utilisez std::string::size_type Lorsque vous comparez avec la longueur d'un std::string).

Les compilateurs émettent des avertissements sur la comparaison des types signés et non signés, car les plages des ints signés et non signés sont différentes et, lorsqu'elles sont comparées, les résultats peuvent être surprenants. Si vous devez effectuer une telle comparaison, vous devez convertir explicitement l’une des valeurs en un type compatible avec l’autre, peut-être après vérification de la validité de la conversion. 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();
}
85
Kristopher Johnson

Hier, j’ai eu exactement le même problème hier en résolvant le problème 2-3 dans C++ accéléré. La clé est de changer toutes les variables que vous allez comparer (en utilisant des opérateurs booléens) en types compatibles. Dans ce cas, cela signifie que string::size_type (ou unsigned int, mais comme cet exemple utilise l'ancien, je vais m'en tenir à cela bien que les deux soient techniquement compatibles).

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

Ce qui complique cet exemple, c’est que les différentes variables de remplissage (padsides et padtopbottom), ainsi que tous les compteurs, doivent aussi être changés en string::size_type.

Pour en revenir à votre exemple, le code que vous avez posté ressemblerait à 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 précédente condition, vous obtiendrez l'erreur si vous n'initialisez pas la variable r en tant que string::size_type dans la boucle for. Donc, vous devez initialiser la boucle for en utilisant quelque chose comme:

    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 un string::size_type variable dans le mixage, chaque fois que vous souhaitez effectuer une opération booléenne sur cet élément, tous les opérandes doivent avoir un type compatible pour pouvoir être compilés sans avertissements.

7
neuronet

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

0001 est 1 signé et non signé 1001 est -1 signé et 9 non signé

(J'ai évité toute la question du complément pour plus de clarté des explications! Ce n'est pas exactement comment les ints sont représentés en mémoire!)

Vous pouvez imaginer que cela fait une différence de savoir si vous comparez avec -1 ou avec +9. Dans de nombreux cas, les programmeurs sont tout simplement trop paresseux pour déclarer le comptage des entrées comme non signé (gonfler la tête de boucle for f.i.). Ce n’est généralement pas un problème car avec les entrées, vous devez compter jusqu’à 2 ^ 31 jusqu’à ce que votre bit de signature vous mord. C'est pourquoi ce n'est qu'un avertissement. Parce que nous sommes trop paresseux pour écrire 'unsigned' au lieu de 'int'.

4
AndreasT

Aux extrêmes, un entier non signé peut devenir plus grand qu'un entier.
Par conséquent, le compilateur génère un avertissement. Si vous êtes certain que ce n'est pas un problème, n'hésitez pas à transtyper les types dans le même type pour que l'avertissement disparaisse (utilisez la transtypage C++ pour pouvoir les repérer facilement).

Vous pouvez également définir le même type de variables pour empêcher le compilateur de se plaindre.
Je veux dire, est-il possible d'avoir un remplissage négatif? Si c'est le cas, conservez-le comme un int. Sinon, vous devriez probablement utiliser unsigned int et laisser le flux capturer les situations où l'utilisateur tape un nombre négatif.

4
Martin York

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

8 bits: -1 signé correspond aux 255 bits non signés 16 bits: -1 signé correspond aux mêmes bits que 65535 non signés, 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 l'appel read (2) échoue en raison de l'invalidité du descripteur de fichier (ou d'une autre erreur), cnt sera défini sur -1. Lors de la comparaison avec sizeof (buf), une valeur non signée, l'instruction if () sera fausse car 0xffffffff n'est pas inférieur à sizeof () de certaines structures de données (raisonnables, non concoctées comme étant de taille maximale).

Par conséquent, vous devez écrire ce qui précède si, pour supprimer l’avertissement signé/non signé comme suit:

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

Cela ne fait que parler fort des 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 grandes que vous ne pouvez pas trouver un type de valeur signé qui fonctionne, vous utilisez un processeur trop petit ou des magnitudes trop grandes dans la langue de votre choix. Si, comme avec de l'argent, chaque chiffre compte, il existe des systèmes à utiliser dans la plupart des langues qui vous fournissent des chiffres infinis de précision. C/C++ ne le fait tout simplement pas bien, et vous devez être très explicite sur tout ce qui concerne les types, comme mentionné dans de nombreuses autres réponses ici.

0
Gregg Wonderly

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

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

et ne vous souciez pas des tailles signées/non signées ou différentes

0
burner