c++ - multidimensional - Índice de matriz fuera de límite en C
dynamic array c++ (9)
¿Por qué C
diferencia en caso de índice de matriz fuera de límite
#include <stdio.h>
int main()
{
int a[10];
a[3]=4;
a[11]=3;//does not give segmentation fault
a[25]=4;//does not give segmentation fault
a[20000]=3; //gives segmentation fault
return 0;
}
Entiendo que está intentando acceder a la memoria asignada para procesar o enhebrar en caso de a[11]
o a[25]
y se saldrá de los límites de la pila en caso de a[20000]
.
¿Por qué el compilador o el enlazador no dan un error? ¿No conocen el tamaño de la matriz? Si no, ¿cómo funciona sizeof(a)
correctamente?
C filosofía es siempre confiar en el programador. Y tampoco el control de límites permite que un programa en C se ejecute más rápido.
C no está haciendo esto. El subsistema de memeory virtual del sistema operativo es.
En el caso en que solo está un poco fuera de límite, se está dirigiendo a memeory que está asignado para su programa (en la pila de llamadas de pila en este caso). En el caso de que esté lejos de los límites, se dirige a la memoria que no se le entregó a su programa y el sistema operativo genera un error de segmentación.
En algunos sistemas, también existe un concepto de memoria "grabable" forzada por el sistema operativo, y es posible que intente escribir en memeory que sea suyo, pero que esté marcado como "no grabable".
Como se mencionó anteriormente, algunos compiladores pueden detectar algunos accesos de matriz fuera de límites en tiempo de compilación. Pero la comprobación de límites en tiempo de compilación no atrapará todo:
int a[10];
int i = some_complicated_function();
printf("%d/n", a[i]);
Para detectar esto, se deberían usar verificaciones de tiempo de ejecución, y se evitan en C debido a su impacto en el rendimiento. Incluso con el conocimiento del tamaño de la matriz de a en tiempo de compilación, es decir, sizeof (a), no puede proteger contra eso sin insertar una verificación de tiempo de ejecución.
El problema es que C / C ++ en realidad no hace ninguna comprobación de límites con respecto a las matrices. Depende del sistema operativo para asegurarse de que está accediendo a la memoria válida.
En este caso particular, está declarando una matriz basada en pila. Dependiendo de la implementación particular, el acceso fuera de los límites de la matriz simplemente accederá a otra parte del espacio de pila ya asignado (la mayoría de los SO e hilos reservan una cierta porción de memoria para la pila). Siempre y cuando solo juegue en el espacio de pila preasignado, todo no se bloqueará (tenga en cuenta que no dije trabajo).
Lo que está sucediendo en la última línea es que ahora ha accedido más allá de la parte de memoria asignada para la pila. Como resultado, usted está indexando en una parte de la memoria que no está asignada a su proceso o está asignada de manera solo lectura. El sistema operativo ve esto y envía una falla seg al proceso.
Esta es una de las razones por las que C / C ++ es tan peligroso cuando se trata de la verificación de límites.
El segfault no es una acción prevista de su programa C que le diga que un índice está fuera de límites. Más bien, es una consecuencia involuntaria de un comportamiento indefinido.
En C y C ++, si declaras una matriz como
type name[size];
Solo se le permite acceder a elementos con índices desde 0
hasta size-1
. Cualquier cosa fuera de ese rango causa un comportamiento indefinido. Si el índice estaba cerca del rango, lo más probable es que lea la memoria de su propio programa. Si el índice estuvo fuera de rango, lo más probable es que su programa sea destruido por el sistema operativo. Pero no puedes saber, cualquier cosa puede suceder.
¿Por qué C permite eso? Bueno, la esencia básica de C y C ++ es no proporcionar características si cuestan rendimiento. C y C ++ se han utilizado durante siglos para sistemas críticos de alto rendimiento. C se ha utilizado como lenguaje de implementación para núcleos y programas donde el acceso fuera de los límites de la matriz puede ser útil para obtener un acceso rápido a los objetos que se encuentran adyacentes en la memoria. Tener el compilador prohibir esto sería en vano.
¿Por qué no advierte sobre eso? Bueno, puedes poner niveles de advertencia altos y esperar la misericordia del compilador. Esto se llama calidad de implementación (QoI). Si algún compilador utiliza un comportamiento abierto (como comportamiento indefinido) para hacer algo bueno, tiene una buena calidad de implementación en ese sentido.
[js@HOST2 cpp]$ gcc -Wall -O2 main.c
main.c: In function ''main'':
main.c:3: warning: array subscript is above array bounds
[js@HOST2 cpp]$
Si, en cambio, formateara su disco duro al ver la matriz a la que se accede fuera de los límites, lo que sería legal, la calidad de la implementación sería bastante mala. Disfruté leer sobre eso en el documento ANSI C Rationale .
Esa no es una cuestión de C es un problema del sistema operativo. Tu programa tiene cierto espacio de memoria y todo lo que hagas dentro de eso está bien. La falla de segmentación solo ocurre cuando accede a la memoria fuera de su espacio de proceso.
No todos los sistemas operativos tienen espacios de direcciones separados para cada proceso, en cuyo caso puede dañar el estado de otro proceso o del sistema operativo sin previo aviso.
Por lo general, solo obtiene un error de segmentación si intenta acceder a la memoria que su proceso no posee.
Lo que está viendo en el caso de a[11]
(y a[10]
por cierto) es la memoria que posee su proceso , pero que no pertenece a la matriz a a[]
. a[25000]
está tan lejos de a[]
, probablemente esté fuera de tu memoria por completo.
Cambiar a[11]
es mucho más insidioso, ya que afecta silenciosamente a una variable diferente (o el marco de pila que puede causar un error de segmentación diferente cuando la función vuelve).
Según entiendo la pregunta y los comentarios, entiendes por qué pueden pasar cosas malas cuando accedes a la memoria fuera de los límites, pero te preguntas por qué tu compilador en particular no te advirtió.
Los compiladores pueden advertirle, y muchos lo hacen en los niveles de advertencia más altos. Sin embargo, el estándar está escrito para permitir que las personas ejecuten compiladores para todo tipo de dispositivos y compiladores con todo tipo de características, por lo que el estándar requiere lo mínimo y garantiza que las personas puedan realizar un trabajo útil.
Hay algunas veces que el estándar requiere que un cierto estilo de codificación genere un diagnóstico. Hay muchas otras ocasiones en las que el estándar no requiere un diagnóstico. Incluso cuando se requiere un diagnóstico, no conozco ningún lugar donde el estándar indique cuál debería ser la redacción exacta.
Pero no estás completamente fuera de peligro aquí. Si su compilador no lo advierte, Lint puede. Además, hay una serie de herramientas para detectar dichos problemas (en tiempo de ejecución) para matrices en el montón, una de las más famosas es Cerca eléctrica (o DUMA ). Pero incluso Electric Fence no garantiza que atrape todos los errores de desbordamiento.
Solo para agregar lo que dicen otras personas, no puede confiar en que el programa simplemente falle en estos casos, no hay garantía de lo que sucederá si intenta acceder a una ubicación de memoria más allá de los "límites de la matriz". Es lo mismo que si hicieras algo como:
int *p;
p = 135;
*p = 14;
Eso es solo al azar; esto podría funcionar Puede que no. No lo hagas Código para evitar este tipo de problemas.