structs initialize example array c pointers data-structures linked-list

initialize - structs in java



Alineación del puntero de herencia C struct (3)

Fondo

He creado una estructura de datos de la lista vinculada básica principalmente con fines de aprendizaje. Uno de los objetivos de la lista es que puede manejar diferentes estructuras de datos. Por lo tanto, probé la composición de estructuras para simular "herencia" en C. Aquí están las estructuras que forman la base de mi lista vinculada.

typedef struct Link { struct Link* next; struct Link* prev; } Link; typedef Link List;

En mi implementación, he elegido tener un nodo centinela que sirve como la cabeza y la cola de la lista (por lo que Link == List).

Para que la lista realmente maneje los datos, una estructura simplemente incluye la estructura de enlace como primer miembro:

typedef struct { Link link; float data; } Node;

Entonces la lista enlazada se ve así.

┌───┬───┬───┐ ┌───┬───┐ ┌───┬───┬───┐ ... <--->│ P │ N │ D │<--->│ P │ N │<--->│ P │ N │ D │<---> ... └───┴───┴───┘ └───┴───┘ └───┴───┴───┘ End Node myList First Node List myList; Node node1 = {{}, 1.024}; .... Node nodeN = {{}, 3.14}; list_init(&myList) // myList.next = &myList; myList.prev = &myList; list_append(&myList, &node1); .... list_append(&myList, &nodeN);

Pregunta

Para recorrer esta lista, un puntero de Node inicialmente apunta al primer nodo . Luego recorre la lista hasta que apunta al centinela nuevamente y luego se detiene.

void traverse() { Node* ptr; for(ptr = myList.next; ptr != &myList; ptr = ptr->link.next) { printf("%f ", ptr->data); } }

Mi pregunta es con la línea ptr != &myList . ¿Hay un problema de alineación de puntero con esta línea?

El bucle for produce correctamente las advertencias: ( warning: assignment from incompatible pointer type y warning: comparison of distinct pointer types lacks a cast ) que puede silenciarse haciendo lo que dice y emitiendo al Node* . Sin embargo, ¿es esto un DumbThingToDo ™? Nunca ptr->data a ptr->data cuando apunta a &myList ya que el ciclo termina una vez ptr == &myList .

TLDR

En las estructuras C, una Base* puede apuntar a un Derived si Base es el primer miembro en Derived . ¿Puede un Derived* señalar una Base si no se accede a ninguno de los miembros específicos Derived ?

EDITAR : reemplazó las llamadas a funciones relevantes con su código en línea equivalente.


En las estructuras C, una Base * puede apuntar a un Derivado si Base es el primer miembro en Derivado. ¿Puede un Derived * señalar una Base si no se accede a ninguno de los miembros específicos Derivados?

Si quiere decir "puede un Punto derivado * para una Base arbitraria (incluida una que no es un primer miembro de un objeto Derivado)", entonces: Técnicamente, no. Mi comprensión del Informe de defectos n. ° 74 es que los requisitos de alineación pueden ser diferentes.

P: Si una estructura tiene un campo de tipo t, ¿los requisitos de alineación del campo pueden ser diferentes de los requisitos de alineación de objetos del mismo tipo que no son miembros de estructuras? Si la respuesta a (a) es `` sí '''', entonces, cuando corresponda, se debe suponer que las preguntas restantes han sido solicitadas para ambos objetos dentro de estructuras y objetos fuera de las estructuras.

R: La subcláusula 6.1.2.5 dice, ''... los punteros a versiones calificadas o no calificadas de tipos compatibles deberán tener los mismos requisitos de representación y alineamiento''. La subcláusula 6.5.2.1 dice: `Cada miembro de campo no de bit de una estructura o de un objeto de unión está alineado de una manera definida por la implementación apropiada para su tipo. '' Y luego, ''por lo tanto, puede haber un relleno sin nombre dentro de un objeto de estructura, ... según sea necesario para lograr la alineación adecuada''. a) Es posible que una implementación establezca requisitos generalizados para satisfacer la subcláusula 6.1.2.5. Estos requisitos pueden reforzarse aún más utilizando el comportamiento definido por la implementación disponible en la subcláusula 6.5.2.1. Sí, los requisitos de alineación pueden ser diferentes.


Escribí esto en los comentarios a la respuesta de ouah, pero lo presentaré como una respuesta en sí misma.

Es cierto cuando escribe que el código en su pregunta podría tener un problema de alineación en teoría si usted cumple con el estándar de C. Sin embargo, no creo que haya una sola arquitectura en la que su código sea probable (o poco probable) para ejecutarse en cuyo ABI muestre tales patrones de alineación. Al menos ninguno de los que conozco, sin duda, y eso incluye la experiencia con algunas arquitecturas incrustadas y de escritorio diferentes. Tampoco puedo imaginar una arquitectura que obtenga algo de esas propiedades de alineación.

También vale la pena señalar que este patrón se utiliza en la práctica con bastante frecuencia; es algo así como el hecho de que siempre se puede ajustar int en punteros, ya que no está garantizado por ningún estándar multiplataforma, pero prácticamente no existe una plataforma en la que no sea cierto, y la gente lo hace todo el tiempo.


Felicitaciones por su presentación.

Creo que su implementación debería funcionar bien, porque C garantiza que la dirección de una estructura es la dirección de su miembro inicial. Dejando de lado las afirmaciones que realiza C sobre la alineación de los miembros de la estructura, esta garantía debería significar que, siempre que su implementación siempre coloque a Link como primer miembro, esto no debería causar problemas de alineación.

desde here : C99 §6.7.2.1:

13 Dentro de un objeto de estructura, los miembros de campo que no son de bit y las unidades en que residen los campos de bit tienen direcciones que aumentan en el orden en que se declaran. Un puntero a un objeto de estructura, adecuadamente convertido, apunta a su miembro inicial (o si ese miembro es un campo de bits, luego a la unidad en la que reside), y viceversa. Puede haber un relleno sin nombre dentro de un objeto de estructura, pero no al principio

Esto debería ser lo que quiso decir sobre Base * y Derived * , aunque no existe tal cosa en C. puro. Son solo estructuras que tienen el mismo diseño de memoria.

Sin embargo, creo que es un poco frágil implementarlo así, porque Node y Link dependen directamente el uno del otro. Si tuviera que cambiar la estructura de Node, su código se volvería inválido. Por el momento no veo el sentido de tener un struct Link extra, aparte de que solo puedes escribir un nuevo Nodo para un nuevo tipo reutilizando Link.

De hecho, me vino a la mente una implementación de lista enlazada cuando vi su publicación y funciona de una manera muy similar a la que tiene la intención de usar su lista: la lista del kernel

Utiliza el mismo elemento de lista (list_head):

struct list_head { struct list_head *next, *prev; };

Contiene esta función macro:

#define list_for_each_entry(pos, head, member) / for (pos = list_first_entry(head, typeof(*pos), member); / &pos->member != (head); / pos = list_next_entry(pos, member))

Si nos fijamos en la forma en que se implementa la macro, verá que ofrece iteración sobre las entradas de una lista sin saber nada sobre el diseño de las entradas en la lista. Suponiendo que interprete su intención correcta, creo que esto es de la forma en que te gustaría que fuera.