tipos superlativos superioridad irregulares ingles inferioridad igualdad español ejemplos desigualdad comparativos comparativo adjetivos c pointers language-lawyer

superlativos - ejemplos de adjetivos comparativos en ingles



¿Puede una comparación de igualdad de punteros no relacionados evaluar a verdadero? (4)

La Sección 6.5.9 del estándar C con respecto a los operadores == y != Indica lo siguiente:

2 Uno de los siguientes debe contener:

  • ambos operandos tienen tipo aritmético;
  • ambos operandos son punteros a versiones calificadas o no calificadas de tipos compatibles;
  • un operando es un puntero a un tipo de objeto y el otro es un puntero a una versión calificada o no calificada de void; o
  • un operando es un puntero y el otro es una constante de puntero nulo.

...

6 Dos punteros comparan igual si y solo si ambos son punteros nulos, ambos son punteros al mismo objeto (incluyendo un puntero a un objeto y un subobjeto al principio) o función, ambos son punteros a uno pasado el último elemento de la misma el objeto de matriz, o uno es un puntero a uno pasado el final de un objeto de matriz y el otro es un puntero al inicio de un objeto de matriz diferente que pasa inmediatamente después del primer objeto de matriz en el espacio de direcciones. 109)

7 Para los propósitos de estos operadores, un puntero a un objeto que no es un elemento de una matriz se comporta igual que un puntero al primer elemento de una matriz de longitud uno con el tipo de objeto como su tipo de elemento.

Nota 109 a pie de página:

109) Dos objetos pueden estar adyacentes en la memoria porque son elementos adyacentes de una matriz más grande o miembros adyacentes de una estructura sin relleno entre ellos, o porque la implementación optó por colocarlos de esa manera, aunque no estén relacionados . Si las operaciones previas del puntero no válido (como los accesos fuera de los límites de la matriz) producen un comportamiento indefinido, las comparaciones posteriores también producen un comportamiento indefinido.

Esto parece indicar que puede hacer lo siguiente:

int a; int b; printf("a precedes b: %d/n", (&a + 1) == &b); printf("b precedes a: %d/n", (&b + 1) == &a);

Esto debería ser legal ya que estamos usando una dirección un elemento más allá del final de una matriz (que en este caso es un solo objeto tratado como una matriz de tamaño 1) sin desreferenciarlo. Lo que es más importante, se requeriría que una de estas dos declaraciones arrojara como resultado 1 si una de las variables siguió inmediatamente a la otra en la memoria.

Sin embargo, las pruebas no parecieron resolver esto. Dado el siguiente programa de prueba:

#include <stdio.h> struct s { int a; int b; }; int main() { int a; int b; int *x = &a; int *y = &b; printf("sizeof(int)=%zu/n", sizeof(int)); printf("&a=%p/n", (void *)&a); printf("&b=%p/n", (void *)&b); printf("x=%p/n", (void *)x); printf("y=%p/n", (void *)y); printf("addr: a precedes b: %d/n", ((&a)+1) == &b); printf("addr: b precedes a: %d/n", &a == ((&b)+1)); printf("pntr: a precedes b: %d/n", (x+1) == y); printf("pntr: b precedes a: %d/n", x == (y+1)); printf(" x=%p, &a=%p/n", (void *)(x), (void *)(&a)); printf("y+1=%p, &b+1=%p/n", (void *)(y+1), (void *)(&b+1)); struct s s1; x=&s1.a; y=&s1.b; printf("addr: s.a precedes s.b: %d/n", ((&s1.a)+1) == &s1.b); printf("pntr: s.a precedes s.b: %d/n", (x+1) == y); return 0; }

El compilador es gcc 4.8.5, el sistema es CentOS 7.2 x64.

Con -O0 , obtengo el siguiente resultado:

sizeof(int)=4 &a=0x7ffe9498183c &b=0x7ffe94981838 x=0x7ffe9498183c y=0x7ffe94981838 addr: a precedes b: 0 addr: b precedes a: 0 pntr: a precedes b: 0 pntr: b precedes a: 1 x=0x7ffe9498183c, &a=0x7ffe9498183c y+1=0x7ffe9498183c, &b+1=0x7ffe9498183c addr: s.a precedes s.b: 1

Podemos ver aquí que un int tiene 4 bytes y que la dirección de a tiene 4 bytes más allá de la dirección de b , y que x tiene la dirección de a tiempo y tiene la dirección de b . Sin embargo, la comparación &a == ((&b)+1) evalúa como falsa, mientras que la comparación (x+1) == y evalúa como verdadera. Espero que ambos sean ciertos ya que las direcciones que se comparan parecen idénticas.

Con -O1 , obtengo esto:

sizeof(int)=4 &a=0x7ffca96e30ec &b=0x7ffca96e30e8 x=0x7ffca96e30ec y=0x7ffca96e30e8 addr: a precedes b: 0 addr: b precedes a: 0 pntr: a precedes b: 0 pntr: b precedes a: 0 x=0x7ffca96e30ec, &a=0x7ffca96e30ec y+1=0x7ffca96e30ec, &b+1=0x7ffca96e30ec addr: s.a precedes s.b: 1 pntr: s.a precedes s.b: 1

Ahora ambas comparaciones se evalúan como falsas aunque (como antes) la dirección que se compara parece ser la misma.

Esto parece apuntar a un comportamiento indefinido , pero basado en cómo leo el pasaje anterior, parece que esto debería permitirse.

Tenga en cuenta también que la comparación de las direcciones de objetos adyacentes del mismo tipo en una struct imprime el resultado esperado en todos los casos.

¿Estoy malinterpretando algo aquí con respecto a lo que está permitido (lo que significa que es UB), o es esta versión de gcc no conforme en este caso?


¿Puede una comparación de igualdad de punteros no relacionados evaluar a verdadero?

Sí, pero ...

int a; int b; printf("a precedes b: %d/n", (&a + 1) == &b); printf("b precedes a: %d/n", (&b + 1) == &a);

Hay, según mi interpretación del estándar C, tres posibilidades:

  • a precede inmediatamente a b
  • b precede inmediatamente a
  • ni a ni b preceden inmediatamente al otro (podría haber un espacio u otro objeto entre ellos)

Jugué con esto hace algún tiempo y concluí que GCC estaba realizando una optimización no válida en el operador == para los punteros, haciendo que arrojara resultados falsos incluso cuando las direcciones son las mismas, así que envié un informe de error:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63611

Ese error se cerró como un duplicado de otro informe:

gcc.gnu.org/bugzilla/show_bug.cgi?id=61502

Los mantenedores de GCC que respondieron a estos informes de errores parecen opinar que la adyacencia de dos objetos no necesita ser consistente y que la comparación de sus direcciones puede mostrar que son adyacentes o no, dentro de la misma ejecución del programa. Como puede ver en mis comentarios sobre el segundo boleto de Bugzilla, estoy totalmente en desacuerdo. En mi opinión, sin un comportamiento consistente del operador == , los requisitos del estándar para los objetos adyacentes no tienen sentido, y creo que debemos suponer que esas palabras no son meramente decorativas.

Aquí hay un programa de prueba simple:

#include <stdio.h> int main(void) { int x; int y; printf("&x = %p/n&y = %p/n", (void*)&x, (void*)&y); if (&y == &x + 1) { puts("y immediately follows x"); } else if (&x == &y + 1) { puts("x immediately follows y"); } else { puts("x and y are not adjacent"); } }

Cuando lo compilo con GCC 6.2.0, las direcciones impresas de x y y difieren exactamente en 4 bytes en todos los niveles de optimización, pero obtengo y immediately follows x solo a -O0 ; en -O1 , -O2 y -O3 obtengo x and y are not adjacent . Creo que este es un comportamiento incorrecto, pero aparentemente, no va a ser reparado.

clang 3.8.1, en mi opinión, se comporta correctamente, mostrando que x immediately follows y en todos los niveles de optimización. Clang anteriormente tenía un problema con esto; Lo reporté:

https://bugs.llvm.org/show_bug.cgi?id=21327

y fue corregido.

Sugiero no confiar en comparaciones de direcciones de objetos posiblemente adyacentes que se comporten de manera consistente.

(Tenga en cuenta que los operadores relacionales ( < , <= , > , >= ) en punteros a objetos no relacionados tienen un comportamiento indefinido, pero los operadores de igualdad ( == != ) Generalmente se requieren para comportarse de manera coherente.)


¿Puede una comparación de igualdad de punteros no relacionados evaluar a verdadero?

Sí. C especifica cuándo esto es cierto.

Dos punteros comparan igual si y solo si ... o uno es un puntero a uno pasado el final de un objeto de matriz y el otro es un puntero al inicio de un objeto de matriz diferente que pasa inmediatamente después del primer objeto de matriz en el espacio de dirección. C11dr §6.5.9 6

Para ser claros: las variables adyacentes en el código no necesitan estar adyacentes en la memoria, pero pueden serlo.

El siguiente código demuestra que es posible . Utiliza un volcado de memoria de un int* además del convencional "%p" y (void*) .

Sin embargo, el código y la salida de OP no reflejan esto. Dada la parte "compare igual si y solo si" de la especificación anterior, la compilación de IMO, OP no cumple . Adyacente a las variables de memoria p,q , del mismo tipo, o bien &p+1 == &q &p == &q+1 debe ser verdadero.

No hay opinión si los objetos difieren en el tipo: OP no solicita ese IAC.

void print_int_ptr(const char *prefix, int *p) { printf("%s %p", prefix, (void *) p); union { int *ip; unsigned char uc[sizeof (int*)]; } u = {p}; for (size_t i=0; i< sizeof u; i++) { printf(" %02X", u.uc[i]); } printf("/n"); } int main(void) { int b = rand(); int a = rand(); printf("sizeof(int) = %zu/n", sizeof a); print_int_ptr("&a =", &a); print_int_ptr("&a + 1 =", &a + 1); print_int_ptr("&b =", &b); print_int_ptr("&b + 1 =", &b + 1); printf("&a + 1 == &b: %d/n", &a + 1 == &b); printf("&a == &b + 1: %d/n", &a == &b + 1); return a + b; }

Salida

sizeof(int) = 4 &a = 0x28cc28 28 CC 28 00 &a + 1 = 0x28cc2c 2C CC 28 00 <-- same bit pattern &b = 0x28cc2c 2C CC 28 00 <-- same bit pattern &b + 1 = 0x28cc30 30 CC 28 00 &a + 1 == &b: 1 <-- compare equal &a == &b + 1: 0


Los autores de la Norma no intentaron hacerla "a prueba de abogados", y como consecuencia, es algo ambigua. Tal ambigüedad generalmente no será un problema cuando los escritores de compiladores hagan un esfuerzo genuino para mantener el Principio de Menos Asombro, ya que existe un claro comportamiento no asombroso, y cualquier otro comportamiento tendría consecuencias sorprendentes. Por otro lado, sí significa que aquellos compiladores que están más interesados ​​en si las optimizaciones pueden justificarse bajo cualquier lectura del Estándar que en si serán compatibles con el código existente pueden encontrar oportunidades interesantes para justificar la incompatibilidad.

El Estándar no requiere que las representaciones de los indicadores tengan ninguna relación con la arquitectura física subyacente. Sería perfectamente legítimo que un sistema represente cada puntero como una combinación de un asa y un desplazamiento. Un sistema que representara punteros de esta manera podría mover libremente los objetos representados en el almacenamiento físico como lo considere oportuno. En un sistema de este tipo, el primer byte del objeto n. ° 57 puede seguir inmediatamente después del último byte del objeto n. ° 23 en un momento dado, pero puede estar en algún lugar no relacionado en algún otro momento. No veo nada en el estándar que prohíba que una implementación de este tipo informe un puntero "pasado" para el objeto n. ° 23 igual a un puntero al objeto n. ° 57 cuando los dos objetos pasaron a ser adyacentes, y como desiguales cuando no pasaron de ser ser.

Además, bajo la regla de si-y-si, una implementación que estaría justificada para mover objetos de esa manera y tener un operador de igualdad estrafalario, como resultado, se le permitiría tener un operador de igualdad estrafalario, moviéndose o no físicamente los objetos en almacen.

Sin embargo, si una implementación especifica cómo se almacenan los punteros en la RAM, y tal definición sería inconsistente con el comportamiento descrito anteriormente, sin embargo, eso obligaría a la implementación a implementar el operador de igualdad de una manera consistente con esa especificación. Cualquier compilador que desee tener un operador de igualdad estrafalario debe abstenerse de especificar un formato de almacenamiento de puntero que sería inconsistente con dicho comportamiento.

Además, el estándar parecería implicar que si el código observa que si dos punteros con valores definidos tienen una representación idéntica, deben comparar igual. Leer un objeto usando un tipo de carácter y luego escribir esa misma secuencia de valores de tipo de carácter en otro objeto debe producir un objeto equivalente al original; tal equivalencia es una característica fundamental del lenguaje. Si p es un puntero que "acaba de pasar" un objeto, y q es un puntero a otro objeto, y sus representaciones se copian en p2 y q2 , respectivamente, entonces p1 debe comparar igual a p q2 a q . Si las representaciones de tipo de carácter descompuestas de q son iguales, eso implicaría que q2 se escribió con la misma secuencia de valores de tipo de carácter que p1 , lo que, a su vez, implicaría que los cuatro punteros deben ser iguales.

En consecuencia, aunque sería permisible que un compilador tenga una semántica peculiar de igualdad para punteros que nunca están expuestos a código que pueda observar su representación a nivel de byte, tal licencia de comportamiento no se extendería a punteros que están así expuestos. Si una implementación define una directiva o configuración que invita a los compiladores a hacer comparaciones individuales arbitrariamente con informes iguales o desiguales cuando se les da punteros al final de un objeto y el inicio de otro cuya ubicación solo sería observable mediante dicha comparación, la implementación no tendría preocuparse por la conformidad en los casos en que se observan las representaciones del puntero. De lo contrario, sin embargo, incluso si hay casos en los que se permita que las implementaciones conformes tengan una semántica de comparación estrafalaria, eso no significa que las implementaciones de calidad lo hagan a menos que un puntero justo después del final de un objeto tenga un significado diferente. representación de un puntero al comienzo de la siguiente.


int a; int b; printf("a precedes b: %d/n", (&a + 1) == &b); printf("b precedes a: %d/n", (&b + 1) == &a);

es un código perfectamente definido, pero probablemente más por suerte que por juicio.

Puede tomar la dirección de un escalar y establecer un puntero más allá de esa dirección. Entonces &a + 1 es válido, pero &a + 2 no lo es. También se le permite comparar el valor de un puntero del mismo tipo con el valor de cualquier otro puntero válido usando == y != , Aunque la aritmética del puntero solo es válida en las matrices.

Su afirmación de que la dirección de a y b le dice sobre cualquier cosa acerca de cómo se colocan en la memoria es litera. Para que quede claro, no puede "alcanzar" b mediante la aritmética del puntero en la dirección de a .

Como para

struct s { int a; int b; };

El estándar garantiza que la dirección de la struct es la misma que la dirección de a , pero se permite insertar a cantidad arbitraria de relleno entre a y b . Nuevamente, no puede alcanzar la dirección de b con ninguna aritmética de puntero en la dirección de a .