¿Qué significa convertir int en void* o viceversa?

7 minutos de lectura

avatar de usuario
Mathai

¿Qué significa convertir un valor entero a un void* o viceversa desde el punto de vista de la memoria? mi entendimiento es void* es una dirección a un bloque de memoria de longitud no especificada.
Esto parece ser algo así como comparar manzanas con naranjas.

int myval = 5;
void* ptr = (void*)myval;
printf("%d",(int)ptr);

Me di cuenta de que debería haber dado el contexto exacto donde se usa esto.

int main(int argc, char* argv[]) {
long       thread;  /* Use long in case of a 64-bit system */
pthread_t* thread_handles; 

/* Get number of threads from command line */
if (argc != 2) Usage(argv[0]);
thread_count = strtol(argv[1], NULL, 10);  
if (thread_count <= 0 || thread_count > MAX_THREADS) Usage(argv[0]);

thread_handles = malloc (thread_count*sizeof(pthread_t)); 

for (thread = 0; thread < thread_count; thread++)  
  pthread_create(&thread_handles[thread], NULL, Hello, (void*) thread);  

printf("Hello from the main thread\n");

for (thread = 0; thread < thread_count; thread++) 
  pthread_join(thread_handles[thread], NULL); 

free(thread_handles);
return 0;
}  /* main */

/*-------------------------------------------------------------------*/
void *Hello(void* rank) {
long my_rank = (long) rank;  /* Use long in case of 64-bit system */ 

printf("Hello from thread %ld of %d\n", my_rank, thread_count);

return NULL;
}  /* Hello */

Este código es del libro de Peter Pachecho sobre programación paralela.

  • Posible duplicado de: stackoverflow.com/questions/3568069/…

    – Mike Nakis

    23 de diciembre de 2011 a las 17:01

  • Es aún peor … – al menos como comparar manzanas con papas

    – alk

    23 de diciembre de 2011 a las 17:03

avatar de usuario
bobbymcr

Fundición int para void * no tiene sentido y no debe hacerse, ya que intentaría convertir un no puntero en un puntero. citando el estándar C99apartado 6.3.2.3 punto 5:

Un entero se puede convertir en cualquier tipo de puntero. Salvo que se especifique lo contrario, el resultado está definido por la implementación, es posible que no esté correctamente alineado, que no apunte a una entidad del tipo al que se hace referencia y que sea una representación de captura.

pudo emitir int * para void * (cualquier puntero es convertible a void * que se puede considerar como el “tipo base” de todos los punteros).

Fundición void * para int no es portátil y puede estar completamente equivocado dependiendo de la plataforma que utilice (p. ej. void * tal vez 64 bits de ancho y int solo puede ser de 32 bits). Citando de nuevo la norma C99, apartado 6.3.2.3 punto 6:

Cualquier tipo de puntero se puede convertir a un tipo entero. Salvo que se especifique lo contrario, el resultado está definido por la implementación. Si el resultado no se puede representar en el tipo entero, el comportamiento no está definido. El resultado no necesita estar en el rango de valores de ningún tipo entero.

Para solucionar esto, algunas plataformas proporcionan uintptr_t lo que le permite tratar un puntero como un valor numérico del ancho adecuado.

  • Este “cualquier puntero es convertible a void *” es incorrecto. Debería ser “cualquier puntero a un objeto es convertible a void *“. Lanzamiento de punteros de función a void * causa UB.

    – 12431234123412341234123

    13 de enero de 2021 a las 13:59

Ambas cosas void* puntero (o cualquier puntero para el caso) y int son, en términos generales, números. Pueden tener un tamaño de bits diferente, pero es poco probable que el puntero sea más pequeño que int, por lo que hace que la operación sea reversible. Por supuesto, es ilegal y nunca debe desreferenciar el puntero que no tiene una ubicación válida para señalar.

El estándar C especifica que debe ser posible convertir un puntero vacío en un tipo integral, de modo que convertir el tipo integral nuevamente en un puntero vacío produzca el mismo puntero. No estoy seguro de si requiere que la conversión de un puntero nulo en un número entero produzca el valor cero, o si solo los ceros literales numéricos se reconocen como especiales. En muchas implementaciones, el valor entero representa una dirección de hardware, pero el estándar no ofrece tal garantía. Sería muy posible que en el hardware que incluyera una trampa especial para la dirección de hardware 0x12345678, convertir un puntero en un número entero restara 0x12345678 de la dirección del hardware, y convertir un número entero en un puntero agregara 0x12345678 nuevamente (por lo tanto, un valor entero de cero representaría un puntero nulo).

En muchos casos, especialmente cuando se desarrolla para controladores incorporados, el proveedor del compilador especificará explícitamente a qué dirección de hardware se accederá al convertir un valor entero particular en un tipo de puntero. En los procesadores con un solo espacio de direcciones lineales, convertir un valor entero 0x12345678 en un puntero generaría generalmente un puntero que hace referencia a la dirección 0x12345678; el manual de referencia del hardware indicaría si había algo especial en esa ubicación. En los procesadores con más espacios de direcciones “interesantes”, puede ser necesario usar algo diferente a la dirección del hardware como puntero. Por ejemplo, en las PC antiguas de IBM, el búfer de visualización se asignaba a la dirección de hardware 0xB8000, pero en la mayoría de los compiladores, la dirección se expresaría como (más corto*) 0xB8000000.

Esto es como comparar manzanas y naranjas. La única forma en que este código funciona es porque lo está emitiendo explícitamente de un lado a otro.

El compilador de C simplemente está copiando los bytes de int en el espacio para el puntero y viceversa, de la misma manera que puede mantener un carácter en un int.

Esto podría incluso causar que su número se arruine si, por alguna razón, un ‘int’ es más largo que un ‘void *’ en su plataforma.

Si realmente desea tener un número para convertir de enteros a punteros y viceversa, consulte intptr_t. Ese tipo en realidad está diseñado para almacenar tanto números enteros como punteros.

avatar de usuario
fge

Es útil poder convertir un puntero en un tipo entero para la aritmética de punteros. por ejemplo, el offsetof() macro, que calcula el desplazamiento de un miembro dentro de una estructura, necesita este tipo de conversión.

Sin embargo, se debe asegurar que el tipo primitivo utilizado para esto sea capaz de manejar un puntero: aquí, para 64 Linux por ejemplo, cuando se usa gcc, este no es el caso: void * tiene tamaño 8, un int tiene tamaño 4.

los offsetof macro se define como tal:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

Se usa de manera prominente en la implementación de la lista doblemente enlazada del kernel de Linux, definida de manera muy simple como:

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

Esta estructura está incrustada dentro de las estructuras del kernel, como en:

struct something {
    //...
    struct list_head list;
    //...
}

Al recorrer una lista, el código debe tomar un puntero a la estructura de codificación. Esto se hace así (definición simplificada):

#define container_of(ptr, type, member) \
    (type *)((char *)ptr - offsetoff(type, member))
#define list_entry(list, type, member) \
    container_of(list, type, member)

Y en el código, muy a menudo verás:

struct something *s;
list_for_each(listptr, somehead) {
   s = list_entry(listptr, struct something, list);
   //...
}

que simplemente no sería factible sin este tipo de aritmética de macros y punteros. Pero depende mucho de la cadena de herramientas.

  • ¿Qué tiene que ver esto con offsetof. También, offsetof está definida por la plataforma, una implementación del compilador es libre de implementarla de la manera que mejor se adapte. Por ejemplo, gcc implementa esto mediante un intrínseco.

    – Jens Gusted

    23 de diciembre de 2011 a las 21:15

Sería como comparar manzanas con naranjas si se intentara hacer alguna comparación. Pero no lo hay. Básicamente, en la mayoría de las arquitecturas, un int se puede convertir en un puntero vacío sin pérdida de información, y un puntero vacío también se puede convertir de nuevo en un int, nuevamente sin pérdida de información. Por supuesto, no debe intentar desreferenciar ese puntero, porque no apunta a ninguna ubicación de memoria significativa: es solo una variable que contiene el mismo patrón de bits que el patrón de bits que el entero solía contener antes de la conversión.

  • ¿Qué tiene que ver esto con offsetof. También, offsetof está definida por la plataforma, una implementación del compilador es libre de implementarla de la manera que mejor se adapte. Por ejemplo, gcc implementa esto mediante un intrínseco.

    – Jens Gusted

    23 de diciembre de 2011 a las 21:15

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad