Pregunta

¿Hay alguna manera de determinar de forma portátil el límite superior e inferior de los valores de puntero vacío en ANSI C89 / ISO C90? (Actualmente no tengo una copia del estándar conmigo (tengo uno en casa). Por supuesto, si se garantiza que los valores de puntero de vacío no estén firmados, esta tarea es trivial (a través de sizeof (void *)); sin embargo, no puedo recordar si esto está garantizado o no. Puedo pensar en algunos muy ineficientes algoritmos (incrementos hasta desbordamiento, etc.), pero me gustaría saber si alguien tiene una forma relativamente barata (en términos de complejidad temporal) y portátil para calcular estos límites.

--EDIT--

También: ¿Hay alguna forma portátil de determinar la validez de los valores de puntero?

Por qué: Esto surgió en una discusión con un compañero de trabajo y me dejó perplejo. No sé en qué está trabajando, ¡pero solo quiero saber porque estoy interesado! :-)

¿Fue útil?

Solución

No hay una forma portátil de determinar si un puntero determinado es válido o no. Tienes que saber con qué tipo de sistema de memoria estás tratando. Dependiendo del sistema operativo y el procesador, puede haber o no una forma de consultar las tablas de páginas del administrador de memoria virtual para determinar los rangos válidos de punteros.

Por ejemplo, en Linux, puede examinar el archivo especial mmap en / proc para obtener el mapa de memoria virtual de un proceso. Aquí hay un ejemplo de cat leyendo su propio mapa de memoria:

$ cat /proc/self/mmap
08048000-0804c000 r-xp 00000000 09:00 5128276                            /bin/cat
0804c000-0804d000 rw-p 00003000 09:00 5128276                            /bin/cat
0804d000-0806e000 rw-p 0804d000 00:00 0                                  [heap]
f7ca7000-f7e40000 r--p 00000000 09:00 3409654                            /usr/lib/locale/locale-archive
f7e40000-f7e41000 rw-p f7e40000 00:00 0 
f7e41000-f7f68000 r-xp 00000000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f68000-f7f6d000 r--p 00127000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6d000-f7f6f000 rw-p 0012c000 09:00 2654292                            /lib/tls/i686/cmov/libc-2.3.6.so
f7f6f000-f7f72000 rw-p f7f6f000 00:00 0 
f7f83000-f7f85000 rw-p f7f83000 00:00 0 
f7f85000-f7f9a000 r-xp 00000000 09:00 2637871                            /lib/ld-2.3.6.so
f7f9a000-f7f9c000 rw-p 00014000 09:00 2637871                            /lib/ld-2.3.6.so
ff821000-ff836000 rw-p 7ffffffea000 00:00 0                              [stack]
ffffe000-fffff000 r-xp ffffe000 00:00 0                                  [vdso]

Puede ver los rangos de punteros válidos, junto con los bits que indican si la memoria es (r) legible, (w) ritable, e (x) ecutable o (p) reenviada (es decir, no paginada en el disco) .

Otros consejos

Los punteros están garantizados por la especificación como sin signo. Pero, ¿por qué quieres encontrar los límites? " Todo entre 0x00000001 y 0xffffffff " no es realmente una prueba útil, ya que el número de punteros válidos será un pequeño subconjunto de eso.

void * siempre es lo suficientemente grande como para contener un puntero a la memoria direccionable. Cualquier otro uso está estrictamente prohibido por la asociación de béisbol de grandes ligas.

Ejemplo: el dec-10 era una arquitectura de 36 bits con palabras de 36 bits. Sin embargo, las direcciones eran de 18 bits y podía contener 2 punteros en cualquier registro / palabra.

Sí, ese es un ejemplo extremo. Si debe hacer cálculos matemáticos con punteros, sizeof es válido; pero hacer cálculos de puntero en otra cosa que no sea una matriz contigua es más dudoso que dudoso.

Finalmente, nunca use un 'void *' para almacenar un puntero a un objeto o un puntero a miembro en C ++. Muchas implementaciones de compiladores en realidad usan múltiples punteros 'físicos' para implementar múltiples herencias de clases concretas (o parcialmente concretas). En realidad, esto casi nunca aparece porque muy pocas personas usan la herencia múltiple de esta manera, y cuando lo hacen, rara vez cortan y desarman punteros. Cuando aparece, es realmente difícil darse cuenta de lo que sucedió.

Debe discernir el valor entero al que se puede convertir un void * a partir del patrón de bits real en la memoria; la conversión de un void * a un tipo entero puede implicar conversiones!

Suponiendo sizeof (void *) == sizeof (long) , para un void * p lo siguiente puede ser falso:

((long)p) == *((long *)&p)

¡Además, el estándar no especifica si incluso es un tipo entero lo suficientemente grande como para contener los valores de todos los punteros válidos!

Por lo tanto, simplemente no hay una forma portátil de hacer lo que quieres hacer ...

Aparte de una región que corresponde a NULL, no hay ninguna restricción (portátil) en las direcciones de memoria. Un sistema operativo suficientemente reforzado podría utilizar varios mecanismos de CPU / sistema operativo para proporcionar a cada proceso direcciones aleatorias y bien distribuidas con cada llamada a malloc (), y el ejecutable independiente de la posición más ASLR puede permitir que el código se ejecute también desde cualquier dirección.

Sé que en Win32, los punteros de 64 bits tienen signos de extensión. Es divertido inspeccionar un minivolcado de 32 bits desde una máquina de 64 bits si no firma punteros de extensión.

Vea aquí para saber cómo un 64 puntero de bits ( POINTER_64 ) funciona en Win32.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top