c++ - punteros - ¿Está almacenando un puntero no válido de forma automática el comportamiento indefinido?
pointers significado (7)
Obviamente, desreferenciar un puntero no válido genera un comportamiento indefinido. ¿Pero qué hay de simplemente almacenar una dirección de memoria no válida en una variable de puntero?
Considera el siguiente código:
const char* str = "abcdef";
const char* begin = str;
if (begin - 1 < str) { /* ... do something ... */ }
La expresión begin - 1
evalúa una dirección de memoria no válida. Tenga en cuenta que en realidad no desreferenciamos esta dirección, simplemente la usamos en aritmética de puntero para probar si es válida. No obstante, todavía tenemos que cargar una dirección de memoria no válida en un registro.
Entonces, ¿es este comportamiento indefinido? Nunca pensé que lo fuera, ya que una gran cantidad de aritmética de puntero parece depender de este tipo de cosas, y un puntero no es más que un número entero de todos modos. Pero recientemente escuché que incluso el acto de cargar un puntero no válido en un registro es un comportamiento indefinido, ya que ciertas arquitecturas arrojarán automáticamente un error de bus o algo si lo haces. ¿Alguien puede señalarme la parte relevante del estándar C o C ++ que resuelve esto de cualquier manera?
$ 5.7 / 6 - "A menos que ambos punteros apuntan a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz, el comportamiento no está definido.75)"
Resumen, no está definido incluso si no desreferencia el puntero .
Algunas arquitecturas tienen registros dedicados para contener punteros. Se permite que cuelgue el valor de una dirección no asignada en dicho registro. El desbordamiento / desbordamiento de enteros se bloquea. Como C tiene como objetivo trabajar en una amplia variedad de plataformas, los indicadores proporcionan un mecanismo para programar de manera segura circuitos inseguros.
Si sabe que no se ejecutará hardware exótico con características tan delicadas, no necesita preocuparse por lo que no está definido por el idioma. Está bien definido por la plataforma.
Por supuesto, el ejemplo es un estilo pobre y no hay una buena razón para hacerlo.
Cualquier uso de un puntero no válido produce un comportamiento indefinido. No tengo el estándar C aquí en el trabajo, pero consulte "indicadores inválidos" en el Fundamento: http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf
Su código es un comportamiento indefinido por una razón diferente:
la expresión begin - 1
no arroja un puntero inválido. Es un comportamiento indefinido. No está permitido realizar aritmética de puntero más allá de los límites de la matriz en la que está trabajando. Por lo tanto, es la resta en sí misma la que no es válida, y no el acto de almacenar el puntero resultante.
Tengo el borrador del estándar C aquí, y lo hace indefinido por omisión. Define el caso de ptr + I
en 6.5.6 / 8 para
- Si el operando puntero apunta a un elemento de un objeto de matriz, y la matriz es lo suficientemente grande, el resultado apunta a un desplazamiento de elemento desde el elemento original, de modo que la diferencia de los subíndices de los elementos de matriz resultante y original es igual a la expresión entera.
- Además, si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P) +1 señala uno pasado el último elemento del objeto de matriz, y si la expresión Q señala un último elemento de un objeto de matriz, la expresión (Q) -1 apunta al último elemento del objeto de la matriz.
Su caso no se ajusta a ninguno de estos. Tampoco es su matriz lo suficientemente grande como para tener -1
ajustar el puntero para apuntar a un elemento de matriz diferente, ni ninguno de los puntos del puntero original o del resultado es un punto final pasado.
Las respuestas correctas se dieron hace años, pero me parece interesante que la razón de ser de C99 [sec. 6.5.6, últimos 3 párrafos] explica por qué la norma respalda la adición de 1
a un puntero que apunta al último elemento de una matriz ( p+1
):
Un respaldo importante de la práctica extendida es el requisito de que un puntero siempre se puede incrementar justo después del final de una matriz, sin temor a desbordamiento o envolvente.
y por qué p-1
no está respaldado:
En el caso de p-1, por otro lado, un objeto completo tendría que ser asignado antes de la matriz de objetos que p atraviesa, por lo que los bucles de decrecimiento que se ejecutan en la parte inferior de una matriz pueden fallar. Esta restricción permite arquitecturas segmentadas, por ejemplo, para colocar objetos al comienzo de un rango de memoria direccionable.
Entonces, si el puntero p
apunta a un objeto al comienzo de un rango de memoria direccionable, que está respaldado por este comentario, entonces p-1
generaría un subdesbordamiento.
Tenga en cuenta que el desbordamiento de enteros es el ejemplo del estándar para el comportamiento indefinido [seg. 3.4.3], ya que depende del entorno de traducción y el entorno operativo. Creo que es fácil ver que esta dependencia del entorno se extiende al subdesbordamiento del puntero.
Esta es la razón por la cual el estándar explícitamente hace que sea un comportamiento indefinido [en 6.5.6 / 8], como lo señalan otras respuestas aquí. Para citar esa frase:
Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto del arreglo, o uno más allá del último elemento del objeto del arreglo, la evaluación no producirá un desbordamiento; de lo contrario, el comportamiento no está definido.
Ver también [sec. 6.3.2.3, últimos 4 párrafos] del razonamiento C99, que proporciona una descripción más detallada de cómo se pueden generar punteros inválidos y qué efectos puede tener.
Sí, es un comportamiento indefinido. Vea la respuesta aceptada a esta pregunta estrechamente relacionada . Al asignar un puntero no válido a una variable, al comparar un puntero no válido, al convertir un puntero no válido se desencadena un comportamiento no definido.