2009-10-12 16 views
18

Al programar en un lenguaje tipo C, ¿el tipo entero "predeterminado" debe ser int o uint/unsigned int? De manera predeterminada, me refiero a cuando no necesitas números negativos, pero cualquiera de ellos debe ser lo suficientemente grande para los datos que tienes. Puedo pensar en buenos argumentos para ambos:Tipo int predeterminado: ¿Firmado o sin firmar?

signed: Mejor comportamiento matemático, menos posibilidad de comportamiento extraño si intentas ir por debajo de cero en algún caso límite en el que no hayas pensado, generalmente evita mejor los casos de esquineras.

unsigned: Proporciona un poco más de seguridad contra el desbordamiento, en caso de que sus suposiciones sobre los valores sean incorrectas. Sirve como documentación de que el valor representado por la variable debe nunca ser negativo.

Respuesta

13

El Google C++ Style Guide tiene una opinión interesante sobre los enteros sin signo:

(cita sigue :)

En enteros sin signo

Algunas personas, incluyendo algunos autores de libros de texto, recomendamos el uso de los tipos sin signo para representar números que nunca son negativos Esto está pensado como una forma de auto-documentación. Sin embargo, en C, las ventajas de dicha documentación son superadas por los errores reales que puede introducir. Considere:

for (unsigned int i = foo.Length()-1; i >= 0; --i) ... 

¡Este código nunca finalizará! Algunas veces, gcc notará este error y le advertirá, pero a menudo no lo hará. Igualmente pueden ocurrir errores graves al comparar las variables firmadas y no firmadas. Básicamente, el esquema de promoción de tipos de C hace que los tipos sin firmar se comporten de forma diferente a lo que cabría esperar.

Por lo tanto, documente que una variable no es negativa utilizando aserciones. No use un tipo sin firmar.

(fin de la cita)

+6

En realidad, creo que el esquema de promoción de tipos de C causa que * signed * ints se comporte de forma diferente a lo que cabría esperar. Las notas sin firmar se comportan bien, si conoces la aritmética del módulo, mientras que las notas firmadas tienen un comportamiento dependiente de la implementación y conversiones graciosas por todos lados. Pero si solo va a usar uno, para evitar mezclarlos, tendrá que firmarse. Así que estoy de acuerdo con la conclusión de que unsigned int no debe usarse de todos modos, pero en realidad creo que los bucles descendentes no deben usarse de todos modos ... –

+2

Ha pasado mucho tiempo desde que GCC no pudo advertir que 'unsigned> = 0' como siempre verdadero. La guía de Google es engañosa en el mejor de los casos, peligrosa en el peor, ya que un tipo muy común y muy correcto para un iterador de bucle será el tipo sin signo 'size_t'. La prueba correcta para underflow sin signo cuando se cuenta hacia atrás usando un contador de origen cero es 'i! = ~ 0U'. –

3

que tienden a ir con firmado, a menos que sepa lo que necesito sin firmar, como int es típicamente firmada, y se necesita más esfuerzo para escribir unsigned int y uint puede causar otro programador de una breve pausa para pensar en lo que los valores pueden ser .

Por lo tanto, no veo ningún beneficio para simplemente el incumplimiento de un unsigned, ya que la int normal está firmada.

+2

+1 Lo afirmaría con más fuerza: la mayoría de las expectativas de los programadores (especialmente si provienen, por ejemplo, de Java) son que 'int' está firmado por defecto. Sería extraño ver 'int' en el código y mostrar comportamiento sin signo. –

11

Ciertamente firmado. Si el desbordamiento te preocupa, el subdesbordamiento debería preocuparte más, porque ir "por debajo de cero" por accidente es más fácil que por int-max.

"unsigned" debe ser una elección consciente que hace que el desarrollador piense en los riesgos potenciales, solo allí donde esté absolutamente seguro de que nunca se puede volver negativo (ni siquiera accidentalmente) y que necesita el espacio de valor adicional .

+0

+1, gracias, iba a hacer la misma pregunta. Este bit es el factor decisivo para mí: "... que necesita el espacio de valor adicional". –

0

Dudo que haya una muy buena respuesta independiente del idioma para esto. Existen suficientes diferencias entre los idiomas y la forma en que manejan los tipos mixtos que ninguna respuesta va a tener sentido para todos (o incluso para la mayoría).

En los idiomas que uso con más frecuencia, uso firmado a menos que tenga un motivo específico para hacer lo contrario. Eso es principalmente C y C++ sin embargo. En otro idioma, bien podría dar una respuesta diferente.

2

No obtiene mucha 'garantía contra desbordamiento' con unsigned. Es probable que obtengas un comportamiento diferente pero extraño que con la firma, pero un poco más tarde ... ¿Es mejor tener esas suposiciones justo antes de la mano, tal vez?

+0

En C, se define el desbordamiento sin signo; El desbordamiento firmado es * indefinido *. Eso es "seguridad contra desbordamiento" para mí. –

1

Dar una asignación de tipo más específica (como unsigned int) transmite más información sobre el uso de la variable, y puede ayudar al compilador a realizar un seguimiento de las veces cuando se asigna un valor "incorrecto". Por ejemplo, si usa una variable para rastrear el ID de la base de datos de un objeto/elemento, es probable que nunca haya un momento en que el ID sea menor que cero (o uno); en este tipo de caso, en lugar de afirmar ese estado, usar un valor entero sin signo transmite esa declaración a otros desarrolladores, así como al compilador.

4

Como regla general, utilicé entradas sin firmar para contar cosas y firmé entradas para medir cosas.

Si se encuentra decreciendo o restando de un int sin firmar, entonces debe estar en un contexto en el que ya espera tener cuidado de no subdesbordar (por ejemplo, porque está en algún paso de código de bajo nivel desde el final de una cuerda, por lo que, por supuesto, primero se ha asegurado de que la cuerda sea lo suficientemente larga como para soportar esto). Si no se encuentra en un contexto como ese, donde es absolutamente crítico que no baje a cero, entonces debería haber usado un valor firmado.

En mi uso, las entradas sin signo son para valores que no pueden ser negativos (o para uno en un millón de situaciones en las que realmente desea el módulo 2^N aritmética), no para valores que simplemente no son negativos, en la implementación actual, probablemente.

Cuestiones relacionadas