He estado revisando la programación en C y solo hay un par de cosas que me molestan.
Tomemos este código por ejemplo:
int myArray[5] = {1, 2, 2147483648, 4, 5};
int* ptr = myArray;
int i;
for(i=0; i<5; i++, ptr++)
printf("\n Element %d holds %d at address %p", i, myArray[i], ptr);
Sé que un int puede tener un valor máximo de 2,147,483,647 positivo. Entonces, al pasar por eso, ¿se "desborda" a la siguiente dirección de memoria, lo que hace que el elemento 2 aparezca como "-2147483648" en esa dirección? Pero eso no tiene sentido porque en la salida todavía dice que la siguiente dirección tiene el valor 4, luego 5. Si el número se hubiera extendido a la siguiente dirección, ¿no cambiaría eso el valor almacenado en esa dirección? ?
Recuerdo vagamente de la programación en MIPS Assembly y viendo las direcciones cambiar los valores durante el programa paso a paso que los valores asignados a esas direcciones cambiarían.
A menos que recuerde incorrectamente, aquí hay otra pregunta: si el número asignado a una dirección específica es mayor que el tipo (como en myArray [2]), ¿no afecta a los valores almacenados en la dirección posterior?
Ejemplo: tenemos int myNum = 4 mil millones en la dirección 0x10010000. Por supuesto, myNum no puede almacenar 4 mil millones, por lo que aparece como un número negativo en esa dirección. A pesar de no poder almacenar este gran número, no tiene ningún efecto sobre el valor almacenado en la dirección posterior de 0x10010004. ¿Correcto?
Las direcciones de memoria solo tienen suficiente espacio para contener ciertos tamaños de números / caracteres, y si el tamaño supera el límite, entonces se representará de manera diferente (como tratar de almacenar 4 mil millones en el int pero aparecerá como un número negativo) y por lo que no tiene efecto en los números / caracteres almacenados en la siguiente dirección.
Lo siento si me fui por la borda. He tenido un gran pedo cerebral todo el día debido a esto.
fuente
int c = INT.MAXINT; c+=1;
y veo qué pasó con c.Respuestas:
No, no lo hace. En C, las variables tienen un conjunto fijo de direcciones de memoria para trabajar. Si está trabajando en un sistema con 4 bytes
ints
y establece unaint
variable2,147,483,647
y luego agrega1
, la variable generalmente contendrá-2147483648
. (En la mayoría de los sistemas. El comportamiento en realidad no está definido). No se modificarán otras ubicaciones de memoria.En esencia, el compilador no le permitirá asignar un valor que sea demasiado grande para el tipo. Esto generará un error del compilador. Si lo fuerza con un caso, el valor se truncará.
Visto de manera bit a bit, si el tipo solo puede almacenar 8 bits e intenta forzar el valor
1010101010101
con un caso, terminará con los 8 bits inferiores, o01010101
.En su ejemplo, independientemente de lo que haga
myArray[2]
,myArray[3]
contendrá '4'. No hay "derrame". Estás tratando de poner algo que tenga más de 4 bytes, simplemente cortará todo en el extremo superior, dejando los 4 bytes inferiores. En la mayoría de los sistemas, esto resultará en-2147483648
.Desde un punto de vista práctico, solo debes asegurarte de que esto nunca ocurra. Este tipo de desbordamientos a menudo resultan en defectos difíciles de resolver. En otras palabras, si cree que hay alguna posibilidad de que sus valores estén en miles de millones, no lo use
int
.fuente
El desbordamiento de entero firmado es un comportamiento indefinido. Si esto sucede, su programa no es válido. No es necesario que el compilador verifique esto por usted, por lo que puede generar un ejecutable que parece hacer algo razonable, pero no hay garantía de que lo haga.
Sin embargo, el desbordamiento de entero sin signo está bien definido. Envolverá el módulo UINT_MAX + 1. La memoria no ocupada por su variable no se verá afectada.
Ver también https://stackoverflow.com/q/18195715/951890
fuente
int
. Supongo que podrían usar el código Gray o BCD o EBCDIC . No sé por qué alguien diseñaría hardware para hacer aritmética con código Gray o EBCDIC, pero, de nuevo, no sé por qué alguien haríaunsigned
con binario y firmaríaint
con algo que no sea el complemento de 2.Entonces, hay dos cosas aquí:
A nivel de idioma:
Cía:
Para aquellos que quieran un ejemplo de "lo que sea", he visto:
convertirse en:
y sí, esta es una transformación legítima.
Significa que, de hecho, existen riesgos potenciales de sobrescribir la memoria en el desbordamiento debido a alguna extraña transformación del compilador.
Nota: en el uso de Clang o gcc
-fsanitize=undefined
en Debug para activar el Desinfectante de Comportamiento Indefinido que abortará en el desbordamiento / desbordamiento de enteros firmados.O significa que puede sobrescribir la memoria utilizando el resultado de la operación para indexar (sin marcar) en una matriz. Desafortunadamente, esto es mucho más probable en ausencia de detección de subflujo / desbordamiento.
Nota: en el uso de Clang o gcc
-fsanitize=address
en Debug para activar el desinfectante de direcciones que abortará en el acceso fuera de los límites.A nivel de máquina :
Realmente depende de las instrucciones de montaje y la CPU que use:
Add
:Tenga en cuenta que si las cosas suceden en los registros o en la memoria, en ningún caso la CPU sobrescribe la memoria en caso de desbordamiento.
fuente
Para avanzar en la respuesta de @ StevenBurnap, la razón por la que esto sucede se debe a cómo funcionan las computadoras a nivel de máquina.
Su matriz se almacena en la memoria (por ejemplo, en la RAM). Cuando se realiza una operación aritmética, el valor en la memoria se copia en los registros de entrada del circuito que realiza la aritmética (ALU: Aritmetic Logic Unit ), la operación se lleva a cabo en los datos en los registros de entrada, produciendo un resultado en el registro de salida. Este resultado se copia nuevamente en la memoria en la dirección correcta en la memoria, dejando intactas otras áreas de la memoria.
fuente
Primero (suponiendo que el estándar C99), es posible que desee incluir
<stdint.h>
un encabezado estándar y usar algunos de los tipos definidos allí, en particular,int32_t
que es exactamente un entero con signo de 32 bits, ouint64_t
que es exactamente un entero sin signo de 64 bits, y así sucesivamente. Es posible que desee utilizar tipos comoint_fast16_t
por razones de rendimiento.Lea las respuestas de otros que explican que la aritmética sin signo nunca se derrama (o desborda) en ubicaciones de memoria adyacentes. Cuidado con el comportamiento indefinido en firmado desbordamiento .
Luego, si necesita calcular exactamente números enteros enormes (por ejemplo, si desea calcular factorial de 1000 con todos sus 2568 dígitos en decimal), quiere bigints, también conocidos como números de precisión arbitrarios (o bignums). Los algoritmos para la aritmética bigint eficiente son muy inteligentes, y generalmente requieren el uso de instrucciones de máquina especializadas (por ejemplo, algunos agregan palabras con carry, si su procesador tiene eso). Por lo tanto, recomiendo en ese caso utilizar alguna biblioteca bigint existente como GMPlib
fuente