personalizado - mouse no funciona windows 10
El puntero no válido puede volver a ser válido. (7)
De acuerdo, esto parece interpretarse como una pregunta de dos partes hecha por algunas personas.
En primer lugar, existía preocupación si se define el uso del poitner para una comparación.
Como se señala en los comentarios, el mero uso del puntero es UB, ya que $ J.2: dice que el uso del puntero al objeto cuya vida útil ha finalizado es UB.
Sin embargo, si se pasa ese obstáculo (que está bien en el rango de UB, puede funcionar después de todo y lo hará en muchas plataformas), esto es lo que encontré sobre las otras preocupaciones:
Dado que los punteros se comparan igual , el código es válido:
Norma C, §6.5.3.2,4 :
[...] Si se ha asignado un valor no válido al puntero, el comportamiento del operador unario * no está definido.
Aunque una nota al pie en ese lugar dice explícitamente. que la dirección de un objeto después del final de su vida útil es un valor de puntero no válido, esto no se aplica aquí, ya que if
se asegura de que el valor del puntero es la dirección de x
y, por lo tanto, es válido.
Norma C ++, §3.9.2,3:
Si un objeto de tipo T está ubicado en una dirección A, se dice que un puntero de tipo cv T * cuyo valor es la dirección A apunta a ese objeto, independientemente de cómo se obtuvo el valor. [Nota: Por ejemplo, se considerará que la dirección una más allá del final de una matriz (5.7) apunta a un objeto no relacionado del tipo de elemento de la matriz que podría estar ubicado en esa dirección.
El énfasis es mío.
int *p;
{
int x = 0;
p = &x;
}
// p is no longer valid
{
int x = 0;
if (&x == p) {
*p = 2; // Is this valid?
}
}
Acceder a un puntero después de que la cosa a la que apunta se haya liberado es un comportamiento indefinido, pero ¿qué sucede si alguna asignación posterior ocurre en la misma área y comparas explícitamente el puntero antiguo con un puntero a la cosa nueva? ¿Habría importado si uintptr_t
&x
y p
a uintptr_t
antes de compararlos?
(Sé que no está garantizado que las dos variables x
ocupen el mismo lugar. No tengo ninguna razón para hacer esto, pero puedo imaginar, digamos, un algoritmo en el que intersectas un conjunto de punteros que podrían haber sido liberados con un conjunto de definitivamente punteros válidos, eliminando los punteros inválidos en el proceso. Si un puntero previamente invalidado es igual a un puntero bueno conocido, tengo curiosidad por lo que sucedería).
Dejemos de lado el hecho si es válido (y estoy convencido de que no lo es, vea la respuesta de Arne Mertz). Todavía pienso que es académico.
El algoritmo en el que está pensando no produciría resultados muy útiles, ya que solo podría comparar dos punteros, pero no tiene oportunidad de determinar si estos apuntadores apuntan al mismo tipo de objeto o algo completamente diferente. Por ejemplo, un puntero a una struct
podría ser la dirección de un solo char
.
El comportamiento es indefinido. Sin embargo, su pregunta me recuerda a otro caso en el que se estaba empleando un concepto similar. En el caso aludido, había estos hilos que obtendrían diferentes cantidades de veces de CPU debido a sus prioridades. Por lo tanto, el subproceso 1 obtendría un poco más de tiempo porque el subproceso 2 estaba esperando la E / S o algo así. Una vez que se hizo su trabajo, el subproceso 1 escribiría valores en la memoria para que el subproceso dos consumiera. Esto no es "compartir" la memoria de una manera controlada. Se escribiría a la pila de llamadas en sí. Donde se asignarían memoria las variables en el hilo 2. Ahora, cuando el subproceso 2 finalmente llegó a la ejecución, a todas sus variables declaradas nunca se les deberían asignar valores porque las ubicaciones que ocupaban tenían valores válidos. No sé qué hicieron si algo salió mal en el proceso, pero esta es una de las optimizaciones más infernales en el código C que he presenciado.
El ganador # 2 en este concurso de comportamiento indefinido es bastante similar a tu código:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int*)malloc(sizeof(int));
int *q = (int*)realloc(p, sizeof(int));
*p = 1;
*q = 2;
if (p == q)
printf("%d %d/n", *p, *q);
}
Según el post:
Usando una versión reciente de Clang (r160635 para x86-64 en Linux):
$ clang -O realloc.c; ./a.out
1 2
Esto solo se puede explicar si los desarrolladores de Clang consideran que este ejemplo, y el suyo, muestran un comportamiento indefinido.
Funcionaría, si por trabajo utilizas una definición muy liberal, más o menos equivalente a la que no se bloquearía.
Sin embargo, es una mala idea. No puedo imaginar una sola razón por la que sea más fácil cruzar los dedos y esperar que las dos variables locales se almacenen en la misma dirección de memoria que escribir p=&x
nuevo. Si esta es solo una pregunta académica, entonces sí, es válida C, pero no se garantiza que la afirmación if sea verdadera o no en todas las plataformas o incluso en diferentes programas.
Edición: para que quede claro, el comportamiento indefinido es si &x == p
en el segundo bloque. El valor de p
no cambiará, sigue siendo un indicador de esa dirección, esa dirección simplemente ya no te pertenece. Ahora el compilador podría (probablemente lo hará) poner la segunda x
en esa misma dirección (suponiendo que no haya ningún otro código intermedio). Si eso es cierto, es perfectamente legal desreferenciar p
como lo haría &x
, siempre que su tipo sea un puntero a un int o algo más pequeño. Al igual que es legal decir p = 0x00000042; if (p == &x) {*p = whatever;}
p = 0x00000042; if (p == &x) {*p = whatever;}
.
Por mi comprensión de la norma (6.2.4. (2))
El valor de un puntero se vuelve indeterminado cuando el objeto al que apunta (o simplemente pasa) llega al final de su vida útil.
Tienes un comportamiento indefinido cuando comparas
if (&x == p) {
Como cumple con los puntos enumerados en el Anexo J.2:
- Se utiliza el valor de un puntero a un objeto cuya vida útil ha finalizado (6.2.4).
- El valor de un objeto con duración de almacenamiento automático se usa mientras está indeterminado (6.2.4, 6.7.9, 6.8).
Probablemente funcionará con la mayoría de los compiladores, pero aún así es un comportamiento indefinido. Para el lenguaje C, estos x
son dos objetos diferentes, uno ha finalizado su vida útil, por lo que tiene UB.
Más seriamente, algunos compiladores pueden decidir engañarte de una manera diferente a la que esperas.
El estándar C dice
Dos punteros se comparan igual si y solo si ambos son punteros nulos, ambos son punteros al mismo objeto (incluido un puntero a un objeto y un subobjeto al principio) o función, ambos son punteros al pasado del último elemento de la misma matriz objeto, o uno es un puntero a uno más allá del final de un objeto de matriz y el otro es un puntero al inicio de un objeto de matriz diferente que sucede a seguir inmediatamente el primer objeto de matriz en el espacio de direcciones.
Tenga en cuenta en particular la frase "ambos son punteros al mismo objeto". En el sentido de la norma, las dos "x" no son el mismo objeto. Es posible que se realicen en la misma ubicación de memoria, pero esto es a discreción del compilador. Dado que son claramente dos objetos distintos, declarados en diferentes ámbitos, la comparación, de hecho, nunca debería ser cierta. Así que un optimizador podría cortar esa rama completamente.
Otro aspecto que aún no se ha discutido de todo esto es que la validez de esto depende de la "vida útil" de los objetos y no del alcance. Si quisieras agregar un posible salto en ese alcance
{
int x = 0;
p = &x;
BLURB: ;
}
...
if (...)
...
if (something) goto BLURB;
la vida útil se extendería siempre que se pueda alcanzar el alcance de la primera x
. Entonces, todo es un comportamiento válido, pero su prueba siempre será falsa y estará optimizada por un compilador decente.
Por todo lo que ve, es mejor dejarlo en el argumento de UB, y no jugar tales juegos en código real.