Acabo de encontrar otra base de código en el trabajo donde los desarrolladores usan constantemente la dirección del primer elemento de las estructuras al copiar/comparar/configurar, en lugar de la estructura en sí. Aquí hay un ejemplo simple.
Primero hay un tipo de estructura:
typedef struct {
int a;
int b;
} foo_t;
Luego hay una función que hace una copia de tal estructura:
void bar(foo_t *inp)
{
foo_t l;
...
memcpy(&l.a, &inp->a, sizeof(foo_t));
...
}
Yo mismo no escribiría una llamada a memcpy
de esa manera y comencé sospechando que los desarrolladores originales simplemente no entendieron bien los punteros y las estructuras en C. Sin embargo, ahora he visto esto en dos bases de código no relacionadas, sin desarrolladores comunes, así que estoy empezando a dudar. mí mismo.
¿Por qué uno querría usar este estilo?
En lugar de eso:
memcpy(&l.a, &inp->a, sizeof(foo_t));
usted puede hacer eso:
memcpy(&l, inp, sizeof(foo_t));
Si bien puede ser peligroso y engañoso, ambas declaraciones en realidad hacen lo mismo aquí, ya que C garantiza que no hay relleno antes del primer miembro de la estructura.
Pero lo mejor es simplemente copiar los objetos de estructura usando un operador de asignación simple:
l = *inp;
¿Por qué uno querría usar este estilo?
Mi conjetura: ignorancia o mala disciplina.
-
+1 para mejor
l = *inp
. 2do mejor distante sugeridomemcpy(&l, inp, sizeof l)
.– chux – Reincorporar a Monica
4 de noviembre de 2013 a las 21:03
-
¿ignorancia? No estoy tan seguro… Quiero decir, ellos supo (con suerte) que las dos cosas fueran equivalentes y estuvieran garantizadas por la especificación, así que no creo que puedas inferir falta de conocimiento de ese código, pero sólo un mal uso de tal conocimiento (¡que si a menudo es peor!).
– Bakuriu
5 de noviembre de 2013 a las 8:05
Nadie debería hacer eso. Si reorganiza los miembros de la estructura, está en problemas.
Uno no lo haría. Si alguna vez te mudaste a
en la estructura o insertaste miembros antes, introducirías un error de destrucción de memoria.
Este código no es seguro porque reorganizar los miembros de la estructura puede resultar en la memcpy
acceder más allá de los límites de la estructura si el miembro a
ya no es el primer miembro.
Sin embargo, es concebible que los miembros estén ordenados intencionalmente dentro de la estructura y el programador solo quiera copiar un subconjunto de ellos, comienzo con miembro a
y corriendo hasta el final de la estructura. Si ese es el caso, entonces el código se puede hacer seguro con el siguiente cambio:
memcpy(&l.a, &inp->a, sizeof(foo_t) - offsetof(foo_t, a));
Ahora los miembros de la estructura se pueden reorganizar en cualquier orden y esto memcpy
nunca saldrá de los límites.
Ingeniero
En realidad, hay un caso de uso legítimo para esto: construir una jerarquía de clases.
Cuando se tratan las estructuras como instancias de una clase, el primer miembro (es decir, el desplazamiento 0) normalmente será la instancia del supertipo… si existe un supertipo. Esto permite que un elenco simple se mueva entre el uso del subtipo y el supertipo. Muy útil.
En la nota de Darren Stone sobre la intención, esto es esperado al ejecutar OO en el lenguaje C.
En cualquier otro casosugeriría evitar este patrón y acceder directamente al miembro, por las razones ya citadas.
Chris zorro
Es un muy mal hábito. La estructura podría tener otro miembro antepuesto, por ejemplo. Este es un hábito increíblemente descuidado y me sorprende leer que alguien haría esto.
Otros ya lo han notado; el que me jode es este:
struct Foo rgFoo [3];
struct Foo *pfoo = &rgFoo [0];
en vez de
struct Foo *pfoo = rgfoo;
¿Por qué eliminar la referencia de la matriz por índice y luego tomar la dirección nuevamente? Ya es la direccion, la unica diferencia a destacar es que pfoo es tecnicamente
struct Foo *const,
no
struct Foo *.
Sin embargo, solía ver el primero todo el tiempo.
Ninguna de las respuestas a continuación realmente agregó nada a mi conocimiento de C, que era lo que esperaba;)
– Magnus
4 de noviembre de 2013 a las 21:37
Esto es bastante útil específicamente cuando varios tipos de estructura comienzan con el mismo primer elemento, como una especie de pseudo herencia. El código base de CPython usa esto en gran medida con
PyObject
; todos los objetos de Python comienzan con unPyObject
miembro, y por lo general se pasan conPyObject *
punteros para que no tenga que codificar el tipo real del objeto con el que está trabajando.– usuario2357112
5 de noviembre de 2013 a las 4:23
los
memcpy
se vuelve seguro si este es el argumento final:sizeof(foo_t) - offsetof(foo_t, a)
.– Darren Piedra
5 de noviembre de 2013 a las 10:24
Hice cosas como esta un poco cuando estaba aprendiendo por primera vez las estructuras y matrices C. Supongo que tu creencia de por qué estaba sucediendo era bastante acertada.
– Nombre falso
5 de noviembre de 2013 a las 11:14
Comenzaría por no tener una variable de una sola letra nombrada
l
.– Guilherme Bernal
6 de noviembre de 2013 a las 13:39