¿Es importante declarar una variable como no firmada si se sabe que nunca debe ser negativa? ¿Ayuda a prevenir que se ingresen números negativos a una función que no debería tenerlos?La importancia de declarar una variable como no firmada
Respuesta
Declarar variables para valores semánticamente no negativos como unsigned
es un buen estilo y una buena práctica de programación.
Sin embargo, tenga en cuenta que esto no le impide cometer errores. Si es perfectamente legal asignar valores negativos a enteros sin signo, el valor se convierte implícitamente a la forma sin signo de acuerdo con las reglas de la aritmética sin signo. Algunos compiladores pueden emitir advertencias en tales casos, otros lo harán silenciosamente.
También vale la pena señalar que para trabajar con enteros sin signo es necesario conocer algunas técnicas dedicadas sin firmar. Por ejemplo, un ejemplo "clásico" que a menudo se menciona con relación a este tema es al revés iteración
for (int i = 99; i >= 0; --i) {
/* whatever */
}
El ciclo anterior se ve natural con firmado i
, pero no se puede convertir directamente en forma sin firmar, lo que significa que
for (unsigned i = 99; i >= 0; --i) {
/* whatever */
}
realmente no hace lo que está destinado a hacer (en realidad es un ciclo interminable). La técnica apropiada en este caso es bien
for (unsigned i = 100; i > 0;) {
--i;
/* whatever */
}
o
for (unsigned i = 100; i-- > 0;) {
/* whatever */
}
Esto se utiliza a menudo como un argumento en contra de los tipos sin signo, es decir, al parecer las versiones sin firmar por encima del ciclo se ven "no natural" y "ilegible ". En realidad, aunque el problema que estamos tratando aquí es el problema genérico de trabajar cerca del extremo izquierdo de un rango cerrado. Este problema se manifiesta de muchas maneras diferentes en C y C++ (como la iteración hacia atrás sobre una matriz usando la técnica de "puntero deslizante" de iteración hacia atrás sobre un contenedor estándar usando un iterador). Es decir. Independientemente de lo poco elegante que parezcan los ciclos sin signo anteriores, no hay forma de evitarlos por completo, incluso si nunca usa tipos enteros sin signo. Por lo tanto, es mejor aprender estas técnicas e incluirlas en su conjunto de expresiones idiomáticas establecidas.
No impide que las personas utilicen indebidamente su interfaz, pero al menos deberían recibir una advertencia a menos que agreguen un molde de estilo C o static_cast
para que desaparezca (en cuyo caso no puede ayudarlos más).
Sí, hay un valor en esto ya que expresa adecuadamente la semántica que desea.
No necesita modelos de estilo C para descartar (no utilizar) la firma. Es un elenco estático simple. –
observado, gracias ... –
misma hace dos cosas:
1) Se da el doble de la gama de valores de sus valores sin signo. Cuando se "firma" el bit más alto se utiliza como el bit de signo (1 significa negativo, 0 para positivo), cuando "no está firmado" puede usar ese bit para los datos. Por ejemplo, un tipo char
va desde -128 y 127, un unsigned char
va Forma 0 a 255
2) Afecta cómo los >>
actos operador, específicamente cuando la derecha de desplazamiento de valores negativos.
No es realmente un signo de bit. 1 para negativo, pero 0 para no negativo. – csj
@csj: ¿Qué valor asignarías a un bit de signo "real" para cero? La afirmación "0 para positivo" es correcta; "0 significa positivo" no es lo que dijo. – Potatoswatter
Al cumplir con las plataformas C++ no es necesario utilizar el complemento de dos. En la práctica, usar 'unsigned' duplica el rango de valores disponibles. – strager
Al usar unsigned cuando los valores con signo no serán necesarios, además de garantizar que el tipo de datos no represente valores por debajo del límite inferior deseado, aumenta el límite superior máximo. Todas las combinaciones de bits que de otra manera se usarían para representar números negativos, se usan para representar un conjunto mayor de números positivos.
Esto tiene valor por la misma razón que "const
corrección" tiene valor. Si sabe que un valor particular no debería cambiar, declare const
y deje que el compilador lo ayude. Si sabes que una variable siempre debe ser no negativa, entonces declara como unsigned
y el compilador te ayudará a detectar inconsistencias.
(Eso, y se puede expresar números dos veces más grande si se utiliza unsigned int
en lugar de int
en este contexto.)
Una sutileza menor es que se reduce la cantidad de límites de la matriz de pruebas comprobando que podría ser necesario ... por ejemplo en lugar de tener que escribir:
int idx = [...];
if ((idx >= 0)&&(idx < arrayLength)) printf("array value is %i\n", array[idx]);
sólo puede escribir:
unsigned int idx = [...];
if (idx < arrayLength) printf("array value is %i\n", array[idx]);
Si '[...]' arroja un valor negativo, detectará el error en el primer caso. Sin embargo, en el segundo caso, no detectará el error, pero trabajará con otro índice positivo "aleatorio" que resultó del comportamiento sin firmar. Eso es mucho peor. –
@Johannes: No veo eso. Si idx fue negativo, se cambiará a un número sin signo muy grande (más de dos mil millones para 'int's de 32 bits). Suponiendo que 'arrayLength' es un número vagamente razonable, el segundo caso detectará el error. –
@David Bueno, es un problema principal de ese pensamiento, no necesariamente un problema en este caso particular. ¿Qué pasa si en lugar de 'idx' tienes' len' y no hay límite superior? La aplicación del principal aplicado en esta respuesta significa que ya no realiza ningún control, y luego opera en esa gran longitud. No es bueno. –
Las otras respuestas son buenas, pero a veces puede dar lugar a confusión. Que es lo que creo que algunos idiomas han optado por no tener unsigned
tipos enteros.
Por ejemplo, suponga que tiene una estructura que se parece a esto para representar un objeto de imagen:
struct T {
int x;
int y;
unsigned int width;
unsigned int height;
};
La idea es que es imposible tener una anchura negativo. Bueno, ¿qué tipo de datos usas para almacenar el borde derecho del rectángulo?
int right = r.x + r.width; // causes a warning on some compilers with certain flags
y ciertamente todavía no lo protege de cualquier desbordamiento de enteros.Así que en este escenario, aunque width
y height
no pueden ser negativos desde el punto de vista conceptual, no hay ganancia real en hacerlos unsigned
, excepto que se requieren algunos moldes para deshacerse de las advertencias sobre la mezcla de tipos firmados y no firmados. Al final, al menos en casos como este, es mejor simplemente hacerlos todos int
s, después de todo, las probabilidades son que no tiene una ventana lo suficientemente amplia como como que es unsigned
.
Este ejemplo rect es bueno. Considere: En el caso anterior, si 'r.x' es negativo,' r.x + r.width' arrojará un resultado totalmente extraño: '-5 + 4u', por ejemplo, arroja' UINT_MAX'. –
A veces deseo que los idiomas definan 7, 15, 31, etc. sin firmar.bits enteros; dichos tipos se procesarían como sin firmar, pero las operaciones entre dichos tipos y tipos firmados más grandes (aunque solo sea por un bit) se firmarían. En algunos procesadores, ciertas operaciones pueden ser más rápidas con valores firmados o sin firmar. Por ejemplo, si un procesador de 32 bits siempre muestra los valores de 16 bits al cargar, se podría cargar un valor "sin firmar de 15 bits" en una instrucción; un valor "sin firmar de 16 bits" requeriría dos. Un compilador podría usar el código más corto con el tipo de 15 bits sin signo. – supercat
Es un buen ejemplo de un caso en el que unsigned no tiene sentido, pero la eliminación completa de valores sin firmar de un idioma, a la Java, simplemente causa otros problemas. Varias veces he encontrado errores sutiles y difíciles de identificar en programas Java donde alguien escribió algunas rutinas de E/S binarias que fallaron de maneras extrañas cuando intentaron trabajar con bytes mayores a 0x7F. – Porculus
También evita que tenga que realizar un envío a/sin signo al interactuar con otras interfaces. Por ejemplo:
for (int i = 0; i < some_vector.size(); ++i)
Eso generalmente molestará a cualquiera que necesite compilar sin advertencias.
No impedirá que se ingresen números negativos en una función; en cambio, los interpretará como grandes números positivos. Esto puede ser moderadamente útil si conoce un límite superior para la comprobación de errores, pero debe verificar el error usted mismo. Algunos compiladores emitirán advertencias, pero si está utilizando mucho los tipos sin firmar, es posible que haya demasiadas advertencias para tratar con facilidad. Estas advertencias se pueden cubrir con moldes, pero eso es peor que solo adherirse a tipos firmados.
No utilizaría un tipo sin signo si supiera que la variable no debería ser negativa, sino más bien si no fuera así.size_t
es un tipo sin firmar, por ejemplo, ya que un tipo de datos simplemente no puede tener un tamaño negativo. Si un valor podría ser negativo pero no debería serlo, es más fácil expresarlo al tenerlo como un tipo firmado y usar algo como i < 0
o i >= 0
(estas condiciones aparecen como false
y true
respectivamente si i
es un tipo sin firmar, independientemente de de su valor).
Si le preocupa la conformidad estándar estricta, puede ser útil saber que los desbordamientos en la aritmética sin signo están completamente definidos, mientras que en la aritmética con signo son un comportamiento indefinido.
Un argumento contrario al uso de unsigned
es que puede encontrarse en situaciones muy razonables en las que se torna incómodo y se introducen errores involuntarios. Considere una clase, por ejemplo, una clase de lista o algo así, con el siguiente método:
unsigned int length() { ... }
Parece muy razonable. Pero a continuación, cuando se quiere iterar sobre ella, se obtiene lo siguiente:
for (unsigned int i = my_class.length(); i >= 0; --i) { ... }
su bucle no terminará y ahora se ve obligado a emitir o hacer alguna otra incomodidad.
Una alternativa al uso de unsigned
es solo a assert
que sus valores no son negativos.
¿Es importante declarar una variable como no firmada si se sabe que nunca debe ser negativa?
Ciertamente no es importante. Algunas personas (Stroustrup y Scott Meyers, ver "Unsigned vs signed - Is Bjarne mistaken?") rechazan la idea de que una variable no debería estar firmada solo porque representa una cantidad sin firmar. Si el punto de usar unsigned
sería indicar que una variable solo puede almacenar valores no negativos, debe verificarlo de alguna manera. De lo contrario, lo único que consigue es
- Un tipo que se esconde en silencio errores, ya que no permite que los valores negativos para exponer
- doble del rango positivo del correspondiente tipo firmado
- Definido desbordamiento/bit-shift/etc semántica
Ciertamente no impiden a las personas el suministro de valores negativos a su función, y el compilador no será capaz de advertirle sobre cualquiera de estos casos (pensar en un tiempo de ejecución negativo siendo int-valor basado aprobado). ¿Por qué no afirmar en la función en su lugar?
assert((idx >= 0) && "Index must be greater/equal than 0!");
El tipo sin signo presenta muchas trampas también. Usted tiene que tener cuidado cuando se utiliza en los cálculos que pueden ser temporales menor que cero (bucle hacia abajo contando, o algo así) y, especialmente, las promociones automáticas que ocurren en las C y C idiomas ++ entre sin firmar y valores con signo
// assume idx is unsigned. What if idx is 0 !?
if(idx - 1 > 3) /* do something */;
"¿Por qué no afirmar en la función"? Porque el tiempo de compilación es mejor que el tiempo de ejecución y es bueno dejar de lado las afirmaciones. Como ha señalado, el uso de 'unsigned' no hace que desaparezca' assert', pero hace que la condición sea más simple: 'x
Muchos, si no la mayoría de los compiladores, pueden advertir sobre las conversiones firmadas a las no firmadas. Esto una vez salvó mi trasero. – Potatoswatter
@Konrad Mi punto es que el uso de 'unsigned' hará que la función no pueda detectar el error porque el parámetro en la función siempre es positivo por definición.El compilador no puede advertirle en todos los casos y, a veces, solo enviar a 'unsigned' en el lado de la llamada solo para deshacerse de una advertencia no solucionará ningún error; en su lugar, un valor negativo se ajustará silenciosamente. –
La combinación de tipos firmados y sin firmar puede ser un gran dolor de cabeza. El código resultante a menudo será inflado, incorrecto o ambos (*).En muchos casos, a menos que necesite almacenar valores entre 2,147,483,648 y 4,294,967,295 dentro de una variable de 32 bits, o si necesita trabajar con valores superiores a 9,223,372,036,854,775,807, le recomiendo que no se moleste en absoluto con los tipos sin firmar.
(*) Lo que debería ocurrir, por ejemplo, si un programador hace:
{ Question would be applicable to C, Pascal, Basic, or any other language } If SignedVar + UnsignedVar > OtherSignedVar Then DoSomething;
Creo edad Pascal de Borland se ocuparía de la situación anterior mediante la conversión de SignedVar y UnsignedVar a un tipo con signo más grande (con el apoyo de la mayor tipo, por cierto, se firmó, por lo que cada tipo sin firmar podría convertirse en uno más grande firmado). Esto produciría código grande, pero sería correcto. En C, si una variable con signo es negativa, es probable que el resultado sea numéricamente incorrecto incluso si UnsignedVar tiene cero. Muchos otros malos escenarios existen también.
Nunca he encontrado código de hinchazón (o dolores de cabeza, incluso menores) relacionados con valores sin signo. Hace código más explícito, sin duda. Pero eso es * bueno *. –
@Konrad Rudolph: si todas las variables no están firmadas, las cosas funcionan de manera predecible. Pero, ¿para qué valores de variables se ejecutará la condición anterior? El código no es exactamente complicado, pero en muchos idiomas su comportamiento exacto es. – supercat
@supercat: fácil, advertirá (o, con la configuración de mi compilador: no compilar) debido a la comparación firmada/no firmada. Ese es el comportamiento que esperaba y el que obtuve. –
Hay dos cosas principales que utilizan unsigned
le da
que le permite utilizar el derecho de operador de desplazamiento
>>
con seguridad en cualquier valor, ya que no puede ser negativo - el uso de un desplazamiento a la derecha en una el valor negativo no está definido.le da la modificación aritmética 2^n aritmética. Con valores firmados, el efecto de underflow/overflow no está definido. Con los valores sin signo obtienes la aritmética mod 2^n, por lo que
0U - 1U
siempre te dará el mayor valor posible sin signo (que siempre será 1 menos que la potencia de 2).
Como acotación al margen, que no suelen utilizar int
o unsigned int
, en lugar utilizo cualquiera {int16_t
, int32_t
, ...} o {uint16_t
, uint32_t
, ...}. (Debe incluir stdint.h
para usarlos). No estoy seguro de cómo lo encuentran mis colegas, pero trato de transmitir el tamaño de la variable de esta manera. En algunos lugares, trato de ser más descarado haciendo algo como: typedef uint32_t Counter32;
- 1. interpretan como no firmada firmado
- 2. R Random Forests Importancia de la variable
- 3. Pregunta de Char C acerca de la codificación firmada/no firmada
- 4. debe declarar la variable escalar
- 5. Importancia de declarar un controlador de eventos de WPF como 'asíncrono' en C# 5
- 6. ¿Cuál es la mejor manera de declarar una variable global?
- 7. variable local de Javascript declarar
- 8. Cómo declarar una variable estática pero no definirla
- 9. Declarar una variable en DB2 SQL
- 10. PHP: ¿por qué no pude declarar la variable const estática?
- 11. ¿Cómo declarar una variable en MySQL?
- 12. Declarar una variable usando un tipo de variable
- 13. Declarar la variable enum en Java bean
- 14. ¿Cómo declarar una variable local en Razor?
- 15. Cómo declarar una variable estática en Javascript
- 16. ¿Cuál es el uso de declarar una variable estática como extern dentro de una función?
- 17. declarar la variable fuera del bucle foreach
- 18. declarar variable en una función sql
- 19. ¿Cómo declarar una variable global en JavaScript?
- 20. ¿Por qué puedo declarar una matriz genérica como una variable de instancia?
- 21. Cómo declarar una variable en una consulta de PostgreSQL
- 22. ASP.NET C# Debe declarar la variable escalar
- 23. ¿La diferencia entre declarar una variable como "var $ foo" o "$ foo" en PHP?
- 24. ¿Por qué no puedo declarar una lista genérica como anulable?
- 25. ¿Cómo declarar una variable dentro de una función de esquema?
- 26. ¿Cómo declarar una variable global desde dentro de una clase?
- 27. ¿Es necesario declarar una AtomicReference como volátil?
- 28. Importancia y especialidad de la función de función importancia
- 29. ¿Es posible declarar una matriz como constante
- 30. declarar propiedad como objeto?
Uso unsigned exclusivamente para la manipulación de bits y en casos más raros para el rango de biggger. Sé cómo codificar con unsigneds, pero no espero que todos mis compañeros de trabajo lo hagan bien todas las veces. PD: la técnica adecuada usa> 0 en lugar de> = 0 ;-). –
@ Peter G .: Fixed. Gracias por mencionarlo. – AnT
@AndreyT: en realidad la iteración inversa está bastante bien manejada por la biblioteca estándar, creo. De todos modos, yo no bucle enteros con frecuencia yo mismo ... –