Comportamiento fuera de límite del índice de matriz

10 minutos de lectura

Comportamiento fuera de limite del indice de matriz
Kazoom

¿Por qué C/C++ se diferencia en el caso de un í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á tratando de acceder a la memoria asignada al proceso o subproceso en caso de a[11] o a[25] y se sale 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, entonces ¿cómo sizeof(a) funciona correctamente?

El problema es que C/C++ en realidad no realiza ninguna verificación de límites con respecto a las matrices. Depende del sistema operativo para asegurarse de que está accediendo a una memoria válida.

En este caso particular, está declarando una matriz basada en pilas. 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 sistemas operativos y subprocesos reservan una cierta porción de memoria para la pila). Mientras esté jugando en el espacio de pila preasignado, todo no fallará (tenga en cuenta que no dije trabajo).

Lo que sucede en la última línea es que ahora ha accedido más allá de la parte de la memoria asignada para la pila. Como resultado, está indexando una parte de la memoria que no está asignada a su proceso o que está asignada en modo de solo lectura. El sistema operativo ve esto y envía una falla de segmentación 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.

  • pero ¿por qué el compilador o el enlazador no dan un error? ¿No conocen el tamaño de la matriz? si no, ¿cómo funciona correctamente sizeof(a)?

    – Kazoom

    22 de marzo de 2009 a las 22:36

  • @Kazoom, C puede saber si un subconjunto muy específico de accesos a matrices es legal. Pero esos superan con creces la cantidad de casos que no se pueden detectar. Supongo que la función no está implementada porque es costosa y solo es útil en un subconjunto de escenarios

    – JaredPar

    22 de marzo de 2009 a las 22:39

  • Como ejemplo de lo anterior, imagine un caso simple de “un[b]=1;”: la verificación de los límites de la matriz tendría que realizarse en tiempo de ejecución y esto costaría ciclos de CPU adicionales para cada (o la mayoría) de las operaciones de la matriz.

    – JC Inácio

    22 de marzo de 2009 a las 22:45

  • @Kazoom, el compilador sabe que la longitud de a es 10 y el tamaño unitario de un int es 4 (por ejemplo), por lo que simplemente usa el valor 40.

    – pax diablo

    23 de marzo de 2009 a las 0:09

  • los verdadero el problema es que C y C++ implementaciones por lo general, no verifica los límites (ni en la compilación ni en el tiempo de ejecución). Están completamente autorizados a hacerlo. No culpes al lenguaje por eso.

    – MSalters

    23 de marzo de 2009 a las 13:56

Comportamiento fuera de limite del indice de matriz
Johannes Schaub – litb

El error de segmento no es una acción intencionada de su programa C que le indicaría que un índice está fuera de los límites. Más bien, es una consecuencia no deseada de un comportamiento indefinido.

En C y C++, si declara una matriz como

type name[size];

Sólo se permite acceder a elementos con índices de 0 hasta size-1. Cualquier cosa fuera de ese rango provoca un comportamiento indefinido. Si el índice estaba cerca del rango, lo más probable es que haya leído la memoria de su propio programa. Si el índice estaba en gran medida fuera de rango, lo más probable es que el sistema operativo elimine su programa. Pero no se puede saber, cualquier cosa puede pasar.

¿Por qué C permite eso? Bueno, la esencia básica de C y C++ es no proporcionar funciones si cuestan rendimiento. C y C ++ se han utilizado durante años 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. Hacer que el compilador prohíba esto sería en vano.

¿Por qué no avisa de eso? Bueno, puede poner niveles de advertencia altos y esperar la misericordia del compilador. Se llama calidad de implementación (CdI). Si algún compilador usa un comportamiento abierto (como un 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 que se accede a la matriz fuera de los límites, lo que sería legal para él, la calidad de la implementación sería bastante mala. Disfruté leer sobre esas cosas en el Justificación ANSI C documento.

  • Eliminé mi propia publicación, fuiste anterior y proporcionaste la respuesta más amplia 🙂

    – bayda

    22 de marzo de 2009 a las 22:40

  • Surge un problema más complicado con char foo[2][8];dado que ni los estándares C ni C++ parecen excluir intencionalmente las matrices bidimensionales de bytes de los tipos de objetos a los que se puede acceder a todos sus bytes como si fueran matrices de caracteres planos, y parecen tener en cuenta el significado de foo[0][i] como tomar la dirección de foo como un char* y accediendo al elemento en el índice i. pero tambien dicen que foo[0][i] sólo sería válido para i valores inferiores a 8.

    – Super gato

    24 mayo 2021 a las 17:36

1647564073 357 Comportamiento fuera de limite del indice de matriz
paxdiablo

Por lo general, solo obtiene una falla de segmentación si intenta acceder a la memoria que su proceso no posee.

Lo que estás viendo en el caso de a[11] (y a[10] por cierto) es memoria que tu proceso lo hace propia pero no pertenece a la a[] formación. a[25000] está tan lejos de a[]probablemente esté fuera de tu memoria por completo.

Cambiando a[11] es mucho más insidioso ya que afecta silenciosamente una variable diferente (o el marco de la pila que puede causar una falla de segmentación diferente cuando regresa su función).

C no está haciendo esto. El subsistema de memoria virtual del sistema operativo lo es.

En el caso de que solo esté ligeramente fuera del límite, se está dirigiendo a la memoria de que es asignado para su programa (en la pila de llamadas de la pila en este caso). En el caso de que esté muy fuera de los límites, está direccionando la memoria que no se ha entregado a su programa y el sistema operativo está generando una falla de segmentación.

En algunos sistemas, también existe un concepto de memoria “escribible” impuesto por el sistema operativo, y es posible que esté intentando escribir en una memoria de su propiedad pero que está marcada como no grabable.

Solo para agregar lo que dicen otras personas, no puede confiar en que el programa simplemente falla 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 simplemente aleatorio; esto podría funcionar Puede que no. no lo hagas Código para evitar este tipo de problemas.

  • No es el mísmo. Se debe suponer que la desreferenciación de un puntero no inicializado es un puntero aleatorio. Es mucho más probable que no se bloquee el acceso a un elemento más allá del final de una matriz porque los sistemas normalmente asignan una página completa de memoria (4 KB o más) a la vez, dejando algo de espacio después del final de la matriz.

    – nadie

    23 de marzo de 2009 a las 2:25

  • Es el mismo. C no te da tal garantía. Si un sistema funciona de esa manera, está bien, pero ¿y qué? Además, creo que deberías volver a leer lo que escribí, ya que te perdiste por completo el punto. No sé por qué respondiste con esto, estoy perplejo.

    – BobbyShaftoe

    23 de marzo de 2009 a las 2:29

  • p = 135 es un error de tipo, no puede asignar un int a una int*.

    – fredo desbordamiento

    9 de abril de 2012 a las 11:31

Comportamiento fuera de limite del indice de matriz
tung nguyen

Como se mencionó litb, algunos compiladores pueden detectar algunos accesos a matrices fuera de los límites en el momento de la compilación. Pero la verificación de límites en tiempo de compilación no detectará todo:

int a[10];
int i = some_complicated_function();
printf("%d\n", a[i]);

Para detectar esto, se tendrían que 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 tamaño de(a), no puede proteger contra eso sin insertar una verificación de tiempo de ejecución.

  • No es el mísmo. Se debe suponer que la desreferenciación de un puntero no inicializado es un puntero aleatorio. Es mucho más probable que no se bloquee el acceso a un elemento más allá del final de una matriz porque los sistemas normalmente asignan una página completa de memoria (4 KB o más) a la vez, dejando algo de espacio después del final de la matriz.

    – nadie

    23 de marzo de 2009 a las 2:25

  • Es el mismo. C no te da tal garantía. Si un sistema funciona de esa manera, está bien, pero ¿y qué? Además, creo que deberías volver a leer lo que escribí, ya que te perdiste por completo el punto. No sé por qué respondiste con esto, estoy perplejo.

    – BobbyShaftoe

    23 de marzo de 2009 a las 2:29

  • p = 135 es un error de tipo, no puede asignar un int a una int*.

    – fredo desbordamiento

    9 de abril de 2012 a las 11:31

Como entiendo la pregunta y los comentarios, entiendes por qué las cosas malas lata sucede cuando accede a la memoria fuera de los límites, pero se pregunta por qué su compilador en particular no le 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 posible y garantiza que las personas puedan hacer un trabajo útil.

Hay algunas veces que el estándar requiere que un determinado estilo de codificación genere un diagnóstico. Hay varios otros momentos en los 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 diga cuál debería ser la redacción exacta.

Pero no estás completamente afuera en el frío aquí. Si su compilador no le advierte, Lint puede hacerlo. Además, hay una serie de herramientas para detectar tales problemas (en tiempo de ejecución) para matrices en el montón, una de las más famosas es Electric Fence (o DUMA). Pero incluso Electric Fence no garantiza que detectará todos los errores de desbordamiento.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad