algo que escribí hace algún tiempo para la educación con propósitos ...
Considérese el siguiente c-programa:
int q[200];
main(void) {
int i;
for(i=0;i<2000;i++) {
q[i]=i;
}
}
después de compilar y ejecutar ella, un volcado de memoria se produce:
$ gcc -ggdb3 segfault.c
$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
ahora usando gdb para llevar a cabo un análisis post mortem:
$ gdb -q ./a.out core
Program terminated with signal 11, Segmentation fault.
[New process 7221]
#0 0x080483b4 in main() at s.c:8
8 q[i]=i;
(gdb) p i
$1 = 1008
(gdb)
eh, el programa no segfault cuando uno escribió fuera de los 200 elementos asignados, en su lugar se estrelló cuando i = 1008, ¿por qué?
Ingresar páginas.
se puede determinar el tamaño de página de varias maneras en UNIX/Linux, una forma es utilizar el sysconf función del sistema() así:
#include <stdio.h>
#include <unistd.h> // sysconf(3)
int main(void) {
printf("The page size for this system is %ld bytes.\n",
sysconf(_SC_PAGESIZE));
return 0;
}
que da la salida:
El tamaño de página de este sistema es 4096 bytes.
o se puede utilizar el getconf utilidad de comandos como esto:
$ getconf PAGESIZE
4096
post mortem
Resulta que la violación de segmento no se produce en i = 200 pero a i = 1008, veamos por qué. Comenzar GDB para hacer algunas ananlysis post mortem:
$gdb -q ./a.out core
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
[New process 4605]
#0 0x080483b4 in main() at seg.c:6
6 q[i]=i;
(gdb) p i
$1 = 1008
(gdb) p &q
$2 = (int (*)[200]) 0x804a040
(gdb) p &q[199]
$3 = (int *) 0x804a35c
q terminó a las 0x804a35c en la dirección, o más bien, el último byte de q [199] estaba en ese lugar. El tamaño de página es como vimos anteriormente 4096 bytes y el tamaño de palabra de 32 bits de la máquina da como resultado que una dirección virtual se descompone en un número de página de 20 bits y un desplazamiento de 12 bits.
q [] terminó en el número de página virtual:
0x804a = 32842 offset:
0x35c = 860 así que había todavía:
4096 - 864 = 3232 bytes que quedan en ese página de memoria en la que se asignó q []. Ese espacio puede contener:
3232/4 = 808 enteros, y el código tratado como si contuviera elementos de Q en la posición 200 a 1008.
Todos sabemos que esos elementos No existe y el compilador no se quejó, ni tampoco el hw, ya que tenemos permisos de escritura para esa página. Solo cuando i = 1008 hizo q [] se refiere a una dirección en una página diferente para la que no tenía permiso de escritura, la memoria virtual hw detectó esto y activó una segfault.
Un entero se almacena en 4 bytes, lo que significa que esta página contiene 808 (3236/4) elementos falsos adicionales lo que significa que todavía es perfectamente legal acceder a estos elementos desde q [200], q [201] hasta el final hasta el elemento 199 + 808 = 1007 (q [1007]) sin activar un fallo seg. Al acceder a q [1008] ingresa una nueva página para la cual los permisos son diferentes.
El desbordamiento de pila ocurre cuando se asigna demasiada memoria de la pila. En este caso, suponiendo 'sizeof (int) == 4', ha asignado 12 bytes insignificantes de la pila. Tu código está escribiendo más allá del final de una matriz. Eso no es desbordamiento de pila. Es un comportamiento indefinido. –
Viene del mismo lugar donde obtuvo el resto de su RAM, probablemente quien le vendió la computadora. 'arr [3]' significa "designar 3' int' de espacio disponible para mi uso ", no significa" crear 3 'int' de espacio fuera del éter", aunque eso sería una implementación legal si fuera físicamente posible. Estás garabateando sobre cualquier memoria/dirección que se encuentre adyacente a 'arr' (bueno, al lado, pero uno en realidad), que como dice David es UB. Sí, es parte de tu stack (los estándares C y C++ no hablan de stack, pero en la práctica es donde van las variables automáticas). –
@vprajan - He actualizado su título para reflejar la pregunta, ya que aquí hay una buena respuesta para llamar la atención. –