50 votes

Devriez-vous toujours utiliser «int» pour les nombres en C, même s'ils ne sont pas négatifs?

J'utilise toujours un entier non signé pour des valeurs qui ne devraient jamais être négatives. Mais aujourd'hui, j'ai remarqué cette situation dans mon code:

 void CreateRequestHeader( unsigned bitsAvailable, unsigned mandatoryDataSize, 
    unsigned optionalDataSize )
{
    If ( bitsAvailable – mandatoryDataSize >= optionalDataSize ) {
        // Optional data fits, so add it to the header.
    }

    // BUG! The above includes the optional part even if
    // mandatoryDataSize > bitsAvailable.
}
 

Dois-je commencer à utiliser int au lieu de int non signé pour les nombres, même s'ils ne peuvent pas être négatifs?

111voto

Une chose qui n'a pas été mentionné, c'est que l'échange signé/non signé nombre peut conduire à des failles de sécurité. C'est un gros problème, depuis de nombreuses fonctions dans la norme C-bibliothèque/retour unsigned numéros (fread, memcpy, malloc etc. tous prennent size_t paramètres)

Par exemple, prenez la suite anodin exemple (à partir de code réel):

//Copy a user-defined structure into a buffer and process it
char* processNext(char* data, short length)
{
    char buffer[512];
    if (length <= 512) {
        memcpy(buffer, data, length);
        process(buffer);
        return data + length;
    } else {
        return -1;
    }
}

L'air inoffensif, non? Le problème est qu' length est signé, mais est converti en unsigned lorsqu'il est passé à l' memcpy. Ainsi, le réglage de la longueur d' SHRT_MIN permettra de valider l' <= 512 test, mais à cause memcpy copier plus de 512 octets de la mémoire tampon, ce qui permet à un attaquant de remplacer la fonction de l'adresse de retour sur la pile et (après un peu de travail) prendre le contrôle de votre ordinateur!

Vous pouvez naïvement être en disant: "C'est tellement évident que la longueur doit être size_t ou vérifiées afin de s' >= 0, je ne pourrais jamais faire cette erreur". Sauf, je vous garantis que si vous avez déjà écrit quelque chose de non trivial, que vous avez. Les auteurs de Windows, Linux, BSD, Solaris, Firefox, OpenSSL, Safari, MS Paint, Internet Explorer, Google Picasa, Opéra, Flash, Open Office, Subversion, Apache, Python, PHP, Pidgin, Gimp, ... et encore et encore ... et ce sont tous lumineux personnes dont le travail est de savoir de sécurité.

En bref, toujours utiliser size_t pour les tailles.

L'homme, la programmation est dur.

29voto

Stephen Points 16714

Dois-je toujours ...

La réponse à "dois-je toujours ..." est presque certainement "non", il y a beaucoup de facteurs qui déterminent si vous devez utiliser un type de données - la cohérence est importante.

Mais, c'est un très subjective de la question, il est vraiment facile de gâcher unsigneds:

for (unsigned int i = 10; i >= 0; i--);

résultats dans une boucle infinie.

C'est pourquoi certains guides de style (Google inclus) décourager unsigned types de données.

Dans mon opinion personnelle, je n'ai pas rencontré de nombreux bugs causés par ces problèmes avec des unsigned types de données, je dirais, utilisez les assertions de vérifier votre code et de les utiliser judicieusement (et moins lorsque vous effectuez l'arithmétique).

15voto

Nietzche-jou Points 7711

Dans certains cas, vous devez utiliser unsigned integer types sont:

  • Vous avez besoin pour traiter une donnée comme une pure représentation binaire.
  • Vous avez besoin de la sémantique de l'arithmétique modulo vous obtenez avec des nombres non signés.
  • Vous avez à l'interface avec le code qui utilise les types non signés (par exemple, la norme de la bibliothèque de routines qui acceptent/retour size_t valeurs.

Mais pour le général, l'arithmétique, la chose est, quand vous dites que quelque chose "ne peut pas être négatif," cela ne veut pas nécessairement dire que vous devez utiliser un type non signé. Parce que vous pouvez mettre une valeur négative dans un unsigned, c'est juste qu'il va devenir une très grande valeur quand vous allez sortir. Donc, si vous voulez dire que les valeurs négatives sont interdites, comme pour une base de la racine carrée de la fonction, alors vous êtes en indiquant une condition préalable de la fonction, et vous devriez le faire valoir. Et vous ne pouvez pas affirmer que ce qui ne peut l'être, est; vous avez besoin d'un moyen de tenir out-of-band valeurs de sorte que vous pouvez les tester (c'est le même genre de logique qui sous-tend getchar() retourner un int et pas char.)

En outre, le choix de l'signé-vs-non signé peut avoir des répercussions sur la performance, ainsi. Jetez un oeil à l' (fictive) de code ci-dessous:

#include <stdbool.h>

bool foo_i(int a) {
    return (a + 69) > a;
}

bool foo_u(unsigned int a)
{
    return (a + 69u) > a;
}

Les deux foo's sont les mêmes, sauf pour le type de leurs paramètres. Mais, lorsqu'il est compilé avec c99 -fomit-frame-pointer -O2 -S, vous obtenez:

 .fichier "essai.c"
.texte
 .p2align 4,,15
.globl foo_i
 .type foo_i, @fonction
foo_i:
 movl $1, %eax
ret
 .taille foo_i, .-foo_i
 .p2align 4,,15
.globl foo_u
 .type foo_u, @fonction
foo_u:
 movl 4(%esp), %eax
 leal 69(%eax), %edx
 cmpl %eax, %edx
 seta %al
ret
 .taille foo_u, .-foo_u
 .ident "GCC: (Debian 4.4.4-7) 4.4.4"
 .de la section .remarque.GNU-de la pile",",@progbits

Vous pouvez voir qu' foo_i() est plus efficace que l' foo_u(). C'est parce que unsigned de dépassement de capacité arithmétique est défini par la norme afin d'envelopper autour de," ainsi (a + 69u) peut très bien être plus petit que a si a est très grand, et donc il doit y avoir des code pour ce cas. D'autre part, signé de dépassement de capacité arithmétique est pas défini, donc GCC va aller de l'avant et d'assumer arithmétique signée n'a pas de débordement, et donc, (a + 69) ne peut jamais être inférieur a. Le choix de types non signés sans discernement peut donc limiter l'impact direct sur les performances.

11voto

5ound Points 907

Bjarne Stroustrup, créateur du langage C++, met en garde sur l'utilisation de types non signés dans son livre Le langage de programmation C++:

L'entier non signé types sont idéales pour les utilisations traiter de stockage comme un peu tableau. À l'aide d'un unsigned au lieu d'une int pour gagner un peu plus de représenter les entiers positifs est presque jamais une bonne idée. Essaie de faire en sorte que certaines valeurs sont positives en déclarant les variables non signé sera généralement vaincu par la conversion implicite des règles.

11voto

6502 Points 42700

La réponse est Oui. Le "unsigned int type de C et de C++ n'est pas un "toujours entier positif", quel que soit le nom du type ressemble. Le comportement de C/C++ unsigned ints n'a pas de sens si vous essayez de lire le type "non négatif"... par exemple:

  • La différence de deux unsigned est un nombre non signé (n'a pas de sens si vous l'avez lu comme "La différence entre les deux nombres négatifs est non-négatif")
  • L'ajout d'un int et unsigned int unsigned
  • Il y a une conversion implicite de int unsigned int (si vous lisez unsigned comme "non négatif" c'est l' opposé de conversion qui serait logique)
  • Si vous déclarez une fonction acceptant un unsigned paramètre lorsque quelqu'un passe un négatif int vous obtenez tout simplement qu'implicitement converti en une énorme valeur positive; en d'autres termes à l'aide d'un unsigned type de paramètre n'est pas de vous aider à trouver les erreurs ni au moment de la compilation, ni au moment de l'exécution.

En effet, non signé nombre est très utile pour certains cas parce qu'ils sont des éléments de l'anneau des entiers-modulo-N", N étant une puissance de deux. Unsigned ints sont utiles lorsque vous souhaitez l'utiliser modulo-n arithmétique, ou comme des masques de bits; ils ne sont PAS utiles, car les quantités.

Malheureusement, dans le C et le C++ non signés ont également été utilisés pour représenter les non-négatif quantités pour être en mesure d'utiliser tous les 16 bits lorsque les entiers où ce petit... à ce moment, être en mesure d'utiliser 32k ou 64 ko a été considéré comme une grande différence. J'avais le classer comme un accident historique... vous ne devriez pas essayer de lire une logique, car il n'y a pas de logique.

Par la façon dont, à mon avis, c'était une erreur... si 32k ne sont pas assez puis très bientôt de 64 ko de ne pas être suffisant; abusant de la modulo entier juste parce que d'un bit supplémentaire à mon avis, est d'un coût trop élevé à payer. Bien sûr, il aurait été raisonnable de le faire si une bonne non-type négatif était présent ou défini... mais non signé sémantique est tout à fait inacceptable pour l'utiliser en tant que non-négatif.

Parfois, vous pouvez trouver qui a dit que non signé est bon parce qu'il "documents" que vous ne voulez valeurs non négatives... cependant que la documentation est de valeur que pour ceux qui ne le savent pas vraiment comment unsigned travaille pour le C ou le C++. Pour moi, voir un type non signé utilisée pour les valeurs non négatives signifie simplement que qui a écrit le code ne comprends pas la langue, sur cette partie.

Si vous comprenez vraiment et veulent le "wrapping" comportement des entiers non signés alors qu'ils sont le bon choix (par exemple, j'ai presque toujours utiliser "unsigned char" quand je suis à la manipulation d'octets); si vous n'allez pas utiliser l'emballage comportement (et que le comportement est juste va être un problème pour vous, comme dans le cas de la différence vous le montre), alors c'est un indicateur clair que le type non signé est un mauvais choix et vous devez vous en tenir à la plaine ints.

Cela veut dire que le C++ std::vector<>::size() type de retour est un mauvais choix ? Oui... c'est une erreur. Mais si vous le dites alors soyez prêt à être appelé mauvais noms par ceux qui ne comprennent pas que le "non signé" le nom est juste un nom... ce qu'il compte c'est le comportement et qui est une "modulo n" comportement (et ne serait d'envisager un "modulo n" type de la taille d'un conteneur à un choix judicieux).

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