¿Por qué / cuándo usar 'intptr t' para el tipo de fundición en C?


Tengo una pregunta sobre el uso de intptr_t vs. long int. He observado que el incremento de direcciones de memoria (por ejemplo, a través de la aritmética de puntero manual) difiere según el tipo de datos. Por ejemplo, incrementar un puntero char agrega 1 a la dirección de memoria, mientras que incrementar un puntero int agrega 4, 8 para un doble, 16 para un doble largo, etc...

Al principio hice algo como esto: {[17]]}

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:\n\n" );
printf( "pChar:  %d\n", (int)pChar );
printf( "pFloat:    %d\n", (int)pFloat );

Que compiló y ejecutó muy bien, pero XCode me dio advertencias para mi encasillamiento: "Cast from puntero a entero de diferente tamaño."

Después de algunas búsquedas en Google y atracones (¿es esto último una palabra todavía?), vi que algunas personas recomiendan usar intptr_t:

#include <stdint.h>

...

printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

Que de hecho resuelve los errores. Así que, pensé, a partir de ahora, debería usar intptr_t para encasillar punteros... Pero luego, después de un poco de inquietud, descubrí que podía resolver el problema simplemente reemplazando int por long int:

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

Así que mi pregunta es, ¿por qué es intptr_t útil, y cuándo debe usarse? Parece superfluo en este caso. Claramente, las direcciones de memoria para myChar y myFloat eran demasiado grandes para caber en un int... así que encasillarlos a long ints resolvió el problema.

¿Es que a veces las direcciones de memoria también son demasiado grandes para long int? Ahora que lo pienso, supongo que es posible si tiene > 4 GB de RAM, en cuyo caso las direcciones de memoria podrían exceder 2^32 - 1 (valor máximo para ints largos sin signo... pero C fue creado mucho antes de que fuera imaginable, ¿verdad? O eran ¿son tan clarividentes?

Gracias!

Author: grisaitis, 2011-06-13

4 answers

Aquí está la cosa: en algunas plataformas, int es el tamaño correcto, pero en otras, long es el tamaño correcto. ¿Cómo sabes cuál es la que debes usar? Uno podría tener razón, pero el estándar no garantiza cuál sería (si es cualquiera). Por lo tanto, el estándar proporciona un tipo que se define como el tamaño correcto, independientemente de la plataforma en la que se encuentre. Donde antes tenías que escribir:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

Ahora solo escribe:

#include <stdint.h>

Y cubre tantos más casos. Imagine especializar el fragmento de código anterior para cada plataforma en la que se ejecuta su código.

 35
Author: Chris Lutz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-13 04:05:26

intptr_t es un nuevo invento, creado después de que se imaginaran direcciones de memoria de 64 bits e incluso de 128 bits.

Si alguna vez necesita convertir un puntero en un tipo entero, siempre use intptr_t. Hacer cualquier otra cosa causará problemas innecesarios para las personas que necesitan portar su código en el futuro.

Tomó mucho tiempo eliminar todos los errores con esto en programas como Mozilla/Firefox cuando la gente quería compilarlo en Linux de 64 bits.

 45
Author: Zan Lynx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-13 03:21:49

Primero, intptr_t es solo para punteros de datos (no funciones) y no se garantiza su existencia.

Entonces, no, no debe usarlo con el propósito de imprimir. El %p es para eso. Solo tienes que lanzar tu puntero a (void*) y ahí lo tienes.

Tampoco es bueno para aritmética / acceso a bytes individuales. Lanzar a (unsigned char*) en su lugar.

intptr_t es realmente para las raras ocasiones que usted tiene que interpretar punteros como enteros (que realmente no son). No lo hagas si no debe.

 9
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-13 09:29:04

Podrías hacer tu vida más fácil usando el especificador de conversión p:

printf("%p\n", (void *)foo);

Además, la forma portátil de imprimir una variable de tipo (u)intptr_t es usar las macros PRI*PTR de inttypes.h; lo siguiente es equivalente a usar p en mi plataforma (32 bits):

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

Los moldes a void * son necesarios para la portabilidad completa, pero se pueden omitir en plataformas con representaciones de puntero uniformes.

 8
Author: Christoph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-13 09:32:45