Pregunta

En varios lenguajes de programación modernos (incluidos C++, Java y C#), el lenguaje permite desbordamiento de enteros que ocurra en tiempo de ejecución sin generar ningún tipo de condición de error.

Por ejemplo, considere este método (artificial) de C#, que no tiene en cuenta la posibilidad de desbordamiento/infradesbordamiento.(Para mayor brevedad, el método tampoco maneja el caso en el que la lista especificada es una referencia nula).

//Returns the sum of the values in the specified list.
private static int sumList(List<int> list)
{
    int sum = 0;
    foreach (int listItem in list)
    {
        sum += listItem;
    }
    return sum;
}

Si este método se llama de la siguiente manera:

List<int> list = new List<int>();
list.Add(2000000000);
list.Add(2000000000);
int sum = sumList(list);

Se producirá un desbordamiento en el sumList() método (porque el int tipo en C# es un entero de 32 bits con signo y la suma de los valores en la lista excede el valor del entero con signo máximo de 32 bits).La variable suma tendrá un valor de -294967296 (no un valor de 4000000000);Lo más probable es que esto no sea lo que pretendía el (hipotético) desarrollador del método sumList.

Obviamente, existen varias técnicas que los desarrolladores pueden utilizar para evitar la posibilidad de desbordamiento de enteros, como usar un tipo como el de Java. BigInteger, o el checked palabra clave y /checked cambio de compilador en C#.

Sin embargo, la pregunta que me interesa es por qué estos lenguajes fueron diseñados para permitir de manera predeterminada que ocurran desbordamientos de enteros en primer lugar, en lugar de, por ejemplo, generar una excepción cuando se realiza una operación en tiempo de ejecución que daría como resultado un Desbordamiento.Parece que este comportamiento ayudaría a evitar errores en los casos en que un desarrollador no tiene en cuenta la posibilidad de desbordamiento al escribir código que realiza una operación aritmética que podría provocar un desbordamiento.(Estos lenguajes podrían haber incluido algo así como una palabra clave "no marcada" que podría designar un bloque donde se permite que ocurra un desbordamiento de enteros sin que se genere una excepción, en aquellos casos en los que ese comportamiento sea explícitamente previsto por el desarrollador;C# en realidad tiene esto.)

¿La respuesta simplemente se reduce al rendimiento? Los diseñadores de lenguajes no querían que sus respectivos lenguajes tuvieran por defecto operaciones aritméticas enteras "lentas" donde el tiempo de ejecución necesitaría hacer trabajo adicional para verificar si se producía un desbordamiento, en cada aritmética aplicable. operación, ¿y esta consideración de rendimiento superó el valor de evitar fallas "silenciosas" en el caso de que ocurra un desbordamiento involuntario?

¿Existen también otras razones para esta decisión de diseño del lenguaje, además de las consideraciones de rendimiento?

¿Fue útil?

Solución

En C#, era una cuestión de rendimiento.Específicamente, evaluación comparativa lista para usar.

Cuando C# era nuevo, Microsoft esperaba que muchos desarrolladores de C++ lo adoptaran.Sabían que mucha gente de C++ pensaba que C++ era rápido, especialmente más rápido que los lenguajes que "perdían" tiempo en la gestión automática de la memoria y cosas similares.

Es probable que tanto los adoptantes potenciales como los críticos de revistas obtengan una copia del nuevo C#, lo instalen, creen una aplicación trivial que nadie jamás escribiría en el mundo real, la ejecuten en un bucle cerrado y midan cuánto tiempo tomó.Luego tomarían una decisión para su empresa o publicarían un artículo basado en ese resultado.

El hecho de que su prueba mostrara que C# es más lento que C++ compilado de forma nativa es el tipo de cosas que haría que la gente abandonara C# rápidamente.El hecho de que su aplicación C# detecte automáticamente el desbordamiento o el desbordamiento insuficiente es el tipo de cosas que podrían pasar por alto.Por lo tanto, está desactivado de forma predeterminada.

Creo que es obvio que el 99% de las veces queremos que /checked esté activado.Es un compromiso desafortunado.

Otros consejos

Creo que el rendimiento es una razón bastante buena.Si considera cada instrucción en un programa típico que incrementa un número entero, y si en lugar de la simple operación de sumar 1, tuviera que verificar cada vez si agregar 1 desbordaría el tipo, entonces el costo en ciclos adicionales sería bastante severo.

Trabaja bajo el supuesto de que el desbordamiento de enteros es siempre un comportamiento no deseado.

A veces, el desbordamiento de enteros es un comportamiento deseado.Un ejemplo que he visto es la representación de un valor de rumbo absoluto como un número de punto fijo.Dado un int sin signo, 0 es 0 o 360 grados y el entero sin signo máximo de 32 bits (0xffffffff) es el valor más grande justo por debajo de 360 ​​grados.

int main()
{
    uint32_t shipsHeadingInDegrees= 0;

    // Rotate by a bunch of degrees
    shipsHeadingInDegrees += 0x80000000; // 180 degrees
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees, overflows 
    shipsHeadingInDegrees += 0x80000000; // another 180 degrees

    // Ships heading now will be 180 degrees
    cout << "Ships Heading Is" << (double(shipsHeadingInDegrees) / double(0xffffffff)) * 360.0 << std::endl;

}

Probablemente existan otras situaciones en las que el desbordamiento sea aceptable, similar a este ejemplo.

C/C++ nunca exige un comportamiento de captura.Incluso la división obvia entre 0 es un comportamiento indefinido en C++, no un tipo específico de trampa.

El lenguaje C no tiene ningún concepto de captura, a menos que cuente las señales.

C++ tiene un principio de diseño que no introduce gastos generales que no están presentes en C a menos que usted lo solicite.Por lo tanto, Stroustrup no habría querido exigir que los números enteros se comportaran de una manera que requiriera una verificación explícita.

Algunos de los primeros compiladores y las implementaciones ligeras para hardware restringido no admiten excepciones en absoluto y, a menudo, las excepciones se pueden deshabilitar con las opciones del compilador.Exigir excepciones para lenguajes integrados sería problemático.

Incluso si C++ hubiera verificado los números enteros, el 99% de los programadores en los primeros días lo habrían desactivado para aumentar el rendimiento...

Porque comprobar si hay desbordamiento lleva tiempo.Cada operación matemática primitiva, que normalmente se traduce en una única instrucción ensambladora, tendría que incluir una verificación de desbordamiento, lo que daría como resultado múltiples instrucciones ensambladoras, lo que potencialmente resultaría en un programa varias veces más lento.

Probablemente tenga un rendimiento del 99%.En x86, habría que comprobar el indicador de desbordamiento en cada operación, lo que supondría un gran impacto en el rendimiento.

El otro 1% cubriría aquellos casos en los que las personas realizan manipulaciones sofisticadas o son "imprecisas" al mezclar operaciones firmadas y no firmadas y desean la semántica de desbordamiento.

La compatibilidad con versiones anteriores es importante.Con C, se suponía que estaba prestando suficiente atención al tamaño de sus tipos de datos para que, si ocurría un desbordamiento o un desbordamiento insuficiente, eso era lo que quería.Luego, con C++, C# y Java, muy poco cambió en la forma en que funcionaban los tipos de datos "integrados".

Mi comprensión de por qué los errores no se generarían de forma predeterminada en tiempo de ejecución se reduce al legado de desear crear lenguajes de programación con un comportamiento similar al ACID.Específicamente, el principio de que cualquier cosa que usted codifique para hacer (o no codificar), lo hará (o no lo hará).Si no codificó algún controlador de errores, entonces la máquina "asumirá" en virtud de que no hay ningún controlador de errores, que realmente desea hacer lo ridículo y propenso a fallar que le está diciendo que haga.

(Referencia ÁCIDA: http://en.wikipedia.org/wiki/ACID)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top