Saltar al contenido

¿Cómo funciona la comparación de punteros en C? ¿Está bien comparar punteros que no apuntan a la misma matriz?

Solución:

Según el estándar C11, los operadores relacionales <, <=, >, y >= solo se puede usar en punteros a elementos de la misma matriz u objeto de estructura. Esto se detalla en la sección 6.5.8p5:

Cuando se comparan dos punteros, el resultado depende de las ubicaciones relativas en el espacio de direcciones de los objetos a los que se apunta. Si dos punteros a tipos de objeto apuntan al mismo objeto, o ambos apuntan uno más allá del último elemento del mismo objeto de matriz, se comparan igual. Si los objetos a los que se apunta son miembros del mismo objeto agregado, los punteros a los miembros de la estructura declarados más tarde comparan los punteros mayores que a los miembros declarados anteriormente en la estructura, y los punteros a los elementos de la matriz con valores de subíndice más grandes comparan los punteros mayores que a los elementos de la misma matriz con valores de subíndice más bajos. Todos los punteros a miembros del mismo objeto de unión se comparan igual. Si la expresión P apunta a un elemento de un objeto de matriz y la expresión Q apunta al último elemento del mismo objeto de matriz, la expresión de puntero Q + 1 compara mayor que P. En todos los demás casos, el comportamiento no está definido.

Tenga en cuenta que cualquier comparación que no satisfaga este requisito invoca un comportamiento indefinido, lo que significa (entre otras cosas) que no puede depender de que los resultados sean repetibles.

En su caso particular, tanto para la comparación entre las direcciones de dos variables locales como entre la dirección de una dirección local y una dinámica, la operación pareció “funcionar”, sin embargo, el resultado podría cambiar al realizar un cambio aparentemente no relacionado en su código o incluso compilar el mismo código con diferentes configuraciones de optimización. Con comportamiento indefinido, solo porque el código podría bloquearse o generar un error no significa que voluntad.

Como ejemplo, un procesador x86 que se ejecuta en modo real 8086 tiene un modelo de memoria segmentada que usa un segmento de 16 bits y un desplazamiento de 16 bits para construir una dirección de 20 bits. Entonces, en este caso, una dirección no se convierte exactamente en un número entero.

Los operadores de igualdad == y != sin embargo, no tiene esta restricción. Se pueden utilizar entre dos punteros a tipos compatibles o punteros NULL. Entonces usando == o != en sus dos ejemplos produciría un código C válido.

Sin embargo, incluso con == y != podría obtener algunos resultados inesperados pero bien definidos. Consulte ¿Puede una comparación de igualdad de punteros no relacionados evaluar como verdadero? para más detalles sobre esto.

Con respecto a la pregunta del examen dada por su profesor, hace una serie de suposiciones erróneas:

  • Existe un modelo de memoria plana donde hay una correspondencia 1 a 1 entre una dirección y un valor entero.
  • Que los valores de puntero convertidos encajen dentro de un tipo entero.
  • Que la implementación simplemente trata a los punteros como números enteros al realizar comparaciones sin explotar la libertad dada por el comportamiento indefinido.
  • Que se usa una pila y que las variables locales se almacenan allí.
  • Que se utiliza un montón para extraer la memoria asignada.
  • Que la pila (y por lo tanto las variables locales) aparece en una dirección más alta que el montón (y por lo tanto los objetos asignados).
  • Las constantes de cadena aparecen en una dirección más baja que el montón.

Si tuviera que ejecutar este código en una arquitectura y / o con un compilador que no satisface estos supuestos, podría obtener resultados muy diferentes.

Además, ambos ejemplos también exhiben un comportamiento indefinido cuando llaman strcpy, ya que el operando derecho (en algunos casos) apunta a un solo carácter y no a una cadena terminada en nulo, lo que hace que la función lea más allá de los límites de la variable dada.

El problema principal al comparar punteros con dos matrices distintas del mismo tipo es que las matrices en sí mismas no necesitan colocarse en una posición relativa particular, una podría terminar antes y después de la otra.

En primer lugar, pensé que quedaría indefinido o de algún tipo o error, porque pt un px no apuntan a la misma matriz (al menos en mi opinión).

No, el resultado depende de la implementación y otros factores impredecibles.

¿También pt> px porque ambos punteros apuntan a variables almacenadas en la pila, y la pila crece hacia abajo, por lo que la dirección de memoria de t es mayor que la de x? ¿Cuál es la razón por la que pt> px es verdadero?

No hay necesariamente una pila. Cuando existe, no necesita disminuir. Podría crecer. Podría ser no contiguo de alguna manera extraña.

Además, creo que la aritmética de punteros entre dos punteros está bien, sin importar a dónde apunten individualmente porque la aritmética solo usa las direcciones de memoria que almacenan los punteros.

Veamos la especificación C, §6.5.8 en la página 85, que trata sobre los operadores relacionales (es decir, los operadores de comparación que está utilizando). Tenga en cuenta que esto no se aplica a los != o == comparación.

Cuando se comparan dos punteros, el resultado depende de las ubicaciones relativas en el espacio de direcciones de los objetos a los que se apunta. … Si los objetos a los que se apunta son miembros del mismo objeto agregado, … los punteros a elementos de matriz con valores de subíndice más grandes se comparan a elementos de la misma matriz con valores de subíndice más bajos.

En todos los demás casos, el comportamiento no está definido.

La última oración es importante. Si bien reduje algunos casos no relacionados para ahorrar espacio, hay un caso que es importante para nosotros: dos matrices, que no forman parte de la misma estructura / objeto agregado1, y estamos comparando punteros con esos dos arreglos. Este es un comportamiento indefinido.

Si bien su compilador acaba de insertar algún tipo de instrucción de máquina CMP (comparar) que compara numéricamente los punteros, y tuvo suerte aquí, UB es una bestia bastante peligrosa. Literalmente, puede suceder cualquier cosa: su compilador podría optimizar toda la función, incluidos los efectos secundarios visibles. Podría generar demonios nasales.

1Los punteros en dos matrices diferentes que son parte de la misma estructura se pueden comparar, ya que esto cae bajo la cláusula donde las dos matrices son parte del mismo objeto agregado (la estructura).

Entonces preguntó qué

p[0].p0 < p[0].p1
p[1].p0 < p[1].p1
p[2].p0 < p[2].p1

Evaluar para. La respuesta es 0, 1 y 0.

Estas preguntas se reducen a:

  1. Es el montón por encima o por debajo de la pila.
  2. Es el montón por encima o por debajo de la sección literal de cadena del programa.
  3. igual que [1].

Y la respuesta a los tres es “implementación definida”. Las preguntas de su profesor son falsas; lo han basado en el diseño tradicional de Unix:

<empty>
text
rodata
rwdata
bss
< empty, used for heap >
...
stack
kernel

pero varios universos modernos (y sistemas alternativos) no se ajustan a esas tradiciones. A menos que hayan precedido la pregunta con “a partir de 1992”; asegúrese de dar un -1 en la evaluación.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *