cast Void pointer en c

Iniciado por prosebas, 19 Abril 2021, 06:56 AM

0 Miembros y 2 Visitantes están viendo este tema.

RayR

Eso de experto lo dije porque esta frase:

¿Al menos sabes por qué la solución que le di le funcionó?:

me había sonado un poco condescendiente, como si fuera demasiado complicado para que alguien (yo) lo entendiera. En fin.

Sólo puntualizar una cosa. No es que almacenar el valor de un puntero en un entero como size_t esté mal en sí. Como había dicho antes, a veces se hace; la propia API de Windows es un ejemplo. Pero aunque en todos los sistemas actuales size_t es lo suficientemente grande para guardar direcciones, no hay garantía de que siempre sea así, y en los casos donde no lo sea, el resultado quedaría indefinido (por ejemplo, en DOS de 16 bits, el programa seguramente no funcionaría correctamente, al menos si se compila para modelo compact, large, etc.) por lo que, si no es estrictamente necesario no se debería hacer.

Usuario887

#11
Cita de: RayR en 22 Abril 2021, 15:35 PM
¿Al menos sabes por qué la solución que le di le funcionó?:

Quise decir: Si contestaste a mi explicacion antes de probar/depurar o al menos echarle un vistazo a la logica de mi planteamiento, entonces...
Nada mas.

Cita de: RayR en 22 Abril 2021, 15:35 PM
Sólo puntualizar una cosa. No es que almacenar el valor de un puntero en un entero como size_t esté mal en sí. Como había dicho antes, a veces se hace; la propia API de Windows es un ejemplo. Pero aunque en todos los sistemas actuales size_t es lo suficientemente grande para guardar direcciones, no hay garantía de que siempre sea así, y en los casos donde no lo sea, el resultado quedaría indefinido (por ejemplo, en DOS de 16 bits, el programa seguramente no funcionaría correctamente, al menos si se compila para modelo compact, large, etc.) por lo que, si no es estrictamente necesario no se debería hacer.

Claro. Por eso le habia dicho al hombre esto:

Cita de: marax en 20 Abril 2021, 11:25 AM
Aqui estas usando una variedad de punteros, innecesarios en cierta manera como añadio MinusFour, sin embargo entiendo que estas experimentando... cosa que yo veo imprescindible para hacerse habil con este tema

El no esta haciendo nada serio. Tu has dicho que eres un habil experto en C. Me imagino que has jugado con el lenguaje de esta forma en el pasado, y eso ha jugado a su vez un papel importante en tu desarrollo. Si no, si eres mas ortodoxo, y en verdad valoras tu tiempo, olvida este hilo porque nunca nos vamos a entender.

Respecto al direccionamiento, eso tambien se lo mencione...

Cita de: marax en 19 Abril 2021, 11:33 AM
Lo que esta imprimiendo printf en tu programa original no es basura, era la direccion de tu variable proof en memoria, con menos precision a causa del casting que hiciste al tipo GeneralNode. size_t es adecuado para estos casos porque se identifica como palabra, es decir, puede contener una direccion a cualquier parte de la memoria (direccionable dentro de tu programa).
(direccionable dentro de tu programa)

De hecho, lo que si que no se es hasta que punto size_t* puede direccionar la memoria. ¿Hablas del caso en que una direccion contenga ademas un segmento?

RayR

No dije ser experto, sólo que algo de experiencia sí que tengo. Estoy de acuerdo en que experimentar y equivocarse es indispensable para aprender, aunque normalmente yo trato de señalar cuando algo no es del todo correcto, sobre todo en cuestiones relacionadas con punteros, que puede ser un tema confuso.

Cita de: marax en 22 Abril 2021, 18:38 PM
Respecto al direccionamiento, eso tambien se lo mencione...
(direccionable dentro de tu programa)

El problema no es si es direccionable o no por el programa. Como había mencionado en otro comentario, aunque es una idea muy extendida, el objetivo de size_t no es almacenar direcciones. Nunca fue creado para eso, y de hecho, existen plataformas donde tiene un tamaño menor a un puntero, por lo que es totalmente posible, y probable, tener variables cuyas direcciones no pueden ser almacenadas en un size_t. Eso es válido, ya que la especificación de C dice expresamente que aunque un puntero puede convertirse a cualquiera de los tipos enteros (incluyendo size_t), puede no caber, y que en ese caso, el comportamiento del programa queda indefinido. En la práctica, en casi todas las arquitecturas actuales sí tiene el tamaño suficiente, y por eso no es tan raro almacenar direcciones en variables enteras (aunque normalmente se usa directamente el long o unsigned long), sin embargo, no hay garantías. Por eso se incorporaron a C los tipos opcionales intptr_t y uintptr_t, los únicos con los que es seguro almacenar punteros (siempre que no sean punteros a funciones). Por cierto, tampoco se requiere que size_t tenga el tamaño de una palabra, aunque eso es lo habitual.

Cita de: marax en 22 Abril 2021, 18:38 PM
De hecho, lo que si que no se es hasta que punto size_t* puede direccionar la memoria. ¿Hablas del caso en que una direccion contenga ademas un segmento?

size_t* puede direccionar la memoria, como el resto de punteros, aunque aquí también, C dice que punteros a diferentes tipos pueden tener tamaños distintos, por lo que en teoría sólo deberían apuntar a memoria donde haya un valor size_t. Claro que en la realidad es prácticamente seguro que todos los punteros tengan el mismo tamaño, exceptuando, otra vez, los punteros a funciones. Ni siquiera void* es válido para apuntar a funciones. Para eso únicamente se deben usar punteros a funciones. Y por supuesto, desreferenciar un puntero que apunta a un dato cuyo tipo no es el mismo que el del puntero es arriesgado. No sólo puede haber problemas de alineación o tamaño, sino que a veces los compiladores, apegándose a lo que dicen las reglas, hacen optimizaciones que rompen código que accede a direcciones por medio de punteros a tipos distintos. Según su rígida interpretación, dado que un puntero de tipo A "no puede" apuntar a un valor de un tipo distinto a A, el compilador no está obligado asegurar que ese acceso se produzca correctamente, y si se rompe algo, toda la culpa es del programador. Eso ha pasado con el kernel Linux, por ejemplo, y hay mucho debate al respecto.

Lo de memorias con segmentos es un ejemplo. En el caso de DOS de 16 bits, para modelo large, por ejemplo, típicamente size_t es de 16 bits, pero los punteros ocupan 32. En este caso específico (x86) sí sería porque contienen segmentos, pero aún en ausencia de segmentación, otras arquitecturas pueden tener punteros más grandes que size_t por motivos diferentes.

MAFUS

Si se me permite un inciso, y por lo que sé, todos los punteros en C (repito: punteros) tienen el mismo tamaño: un tamaño capaz de guardar una dirección de memoria.
Otra cosa es el tipo del puntero: éste le dice al compilador el tamaño del dato al que se apunta para que cuándo se dereferencíe tome tantos bytes de la memoria a partir de la dirección del puntero para trabajar con él.
Es decir, un puntero en sí da igual que sea int, long, unsigned, o una función de muchos parámetros, es solo una dirección de memoria.
Por eso el void* puede convertirse en cualquier cosa, porqué únicamente guarda la información de la dirección, no del tamaño del dato y por eso el compilador necesita el cast, para saber cuántos bytes toma el dato que se guarda allí.
En los sistemas en modo real un puntero puede apuntar a cualquier sitio: a la memoria gráfica, a alguna parte del sistema operativo, a otros programas... y lo peor es que dejaba escribir allí. Por eso durante tanto tiempo los punteros eran una cosa muy temida, no académicamente, sino porqué podían causar auténticos desastres. También los virus tenían más acceso a la máquina.
Ahora con las protecciones propias del procesador y del sistema operativo un puntero mal dirigido lo único que puede conseguir es que el sistema cierre el programa.

MinusFour

Cita de: MAFUS en 26 Abril 2021, 16:04 PM
Si se me permite un inciso, y por lo que sé, todos los punteros en C (repito: punteros) tienen el mismo tamaño: un tamaño capaz de guardar una dirección de memoria.
Otra cosa es el tipo del puntero: éste le dice al compilador el tamaño del dato al que se apunta para que cuándo se dereferencíe tome tantos bytes de la memoria a partir de la dirección del puntero para trabajar con él.
Es decir, un puntero en sí da igual que sea int, long, unsigned, o una función de muchos parámetros, es solo una dirección de memoria.
Por eso el void* puede convertirse en cualquier cosa, porqué únicamente guarda la información de la dirección, no del tamaño del dato y por eso el compilador necesita el cast, para saber cuántos bytes toma el dato que se guarda allí.
En los sistemas en modo real un puntero puede apuntar a cualquier sitio: a la memoria gráfica, a alguna parte del sistema operativo, a otros programas... y lo peor es que dejaba escribir allí. Por eso durante tanto tiempo los punteros eran una cosa muy temida, no académicamente, sino porqué podían causar auténticos desastres. También los virus tenían más acceso a la máquina.
Ahora con las protecciones propias del procesador y del sistema operativo un puntero mal dirigido lo único que puede conseguir es que el sistema cierre el programa.

Hasta donde yo se no hay ninguna garantía que un programa en C pueda usar un puntero void* para almacenar punteros a funciones (los punteros de funciones pueden usar otro tipo de direccionamiento). Había un apartado en la especificación en la que dejaba esto en claro. Hay otros estándares y extensiones que quizás puedan agregar esa limitación pero al menos el estándar en C no ofrece esa garantía.

Esto ya se sale fuera del tema.

RayR

Es tal como dijo MinusFour. Y C++ va aún más lejos. Los punteros a funciones miembro normalmente son totalmente distintos a los otros, incluyendo los punteros a funciones "normales", no miembro. Por ejemplo, en Visual C++, cuando se usa herencia múltiple, estos punteros ocupan el doble de espacio que los normales (128 bits en programas de 64 bits). La explicación se saldría mucho del tema, pero tiene que ver con que los objetos con varias clases padre tienen más de un puntero this. En GCC (al menos en x86 y x86-64) incluso para objetos sin superclases, los punteros a sus funciones miembro ocupan el doble.