¿Por qué usar la dirección del primer elemento de la estructura, en lugar de la estructura misma?

4 minutos de lectura

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?

  • 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 un PyObject miembro, y por lo general se pasan con PyObject * 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


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 sugerido memcpy(&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.

avatar de usuario
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.

avatar de usuario
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.

¿Ha sido útil esta solución?