El estándar C garantiza que size_t
es un tipo que puede contener cualquier índice de matriz. Esto significa que, lógicamente, size_t
debería poder contener cualquier tipo de puntero. Leí en algunos sitios que encontré en Google que esto es legal y / o siempre debería funcionar:
void *v = malloc(10);
size_t s = (size_t) v;
Entonces, en C99, el estándar introdujo los tipos intptr_t
y uintptr_t
, que son tipos con y sin signo garantizados para poder mantener punteros:
uintptr_t p = (size_t) v;
Entonces, ¿cuál es la diferencia entre usar size_t
y uintptr_t
? Ambos no están firmados, y ambos deberían poder contener cualquier tipo de puntero, por lo que parecen funcionalmente idénticos. ¿Existe alguna razón real convincente para usar uintptr_t
(o mejor aún, a void *
) en lugar de una size_t
, aparte de la claridad? En una estructura opaca, donde el campo será manejado solo por funciones internas, ¿hay alguna razón para no hacerlo?
Del mismo modo, ptrdiff_t
ha sido un tipo con signo capaz de contener las diferencias de puntero y, por lo tanto, capaz de contener la mayoría de los punteros, entonces, ¿en qué se diferencia intptr_t
?
¿No están todos estos tipos básicamente sirviendo versiones trivialmente diferentes de la misma función? Si no, ¿por qué? ¿Qué no puedo hacer con uno de ellos que no puedo hacer con otro? Si es así, ¿por qué C99 agregó dos tipos esencialmente superfluos al lenguaje?
Estoy dispuesto a ignorar los indicadores de función, ya que no se aplican al problema actual, pero siéntase libre de mencionarlos, ya que tengo la sospecha de que serán fundamentales para la respuesta "correcta".
size_t
y,uintptr_t
pero ¿qué pasa conptrdiff_t
yintptr_t
, no podrían ambos almacenar el mismo rango de valores en casi cualquier plataforma? ¿Por qué tener tipos enteros de tamaño de puntero con signo y sin signo, especialmente siptrdiff_t
ya tiene el propósito de un tipo de entero de tamaño de puntero con signo?size_t
tener al menos 16 bits, peroptrdiff_t
debe tener al menos 17 bits (lo que en la práctica significa que probablemente tendrá al menos 32 bits).SIZE_MAX
debo imaginar que no debería ser 2 ** 64. Esto está usando el direccionamiento plano, eso sí; no es necesaria la segmentación para tener una discrepancia entreSIZE_MAX
y el rango de un puntero de datos.Con respecto a su declaración:
Esto es en realidad una falacia (una idea errónea resultante de un razonamiento incorrecto) (a) . Puede pensar que este último se desprende del primero, pero ese no es realmente el caso.
Los punteros y los índices de matriz no son lo mismo. Es bastante posible imaginar una implementación conforme que limite las matrices a 65536 elementos pero permita que los punteros dirijan cualquier valor a un espacio de direcciones masivo de 128 bits.
C99 establece que el límite superior de una
size_t
variable está definido porSIZE_MAX
y esto puede ser tan bajo como 65535 (ver C99 TR3, 7.18.3, sin cambios en C11). Los punteros serían bastante limitados si estuvieran restringidos a este rango en los sistemas modernos.En la práctica, probablemente encontrará que su suposición se cumple, pero eso no se debe a que el estándar lo garantice. Porque en realidad no lo garantiza.
(a) Por cierto, esta no es una forma de ataque personal, solo indica por qué sus declaraciones son erróneas en el contexto del pensamiento crítico. Por ejemplo, el siguiente razonamiento tampoco es válido:
La ternura o no de los cachorros no tiene nada que ver aquí, todo lo que digo es que los dos hechos no conducen a la conclusión, porque las dos primeras oraciones permiten la existencia de cosas lindas que no son cachorros.
Esto es similar a su primera declaración, no necesariamente obligando a la segunda.
fuente
ptrdiff_t
vs.intptr_t
).Dejaré que todas las otras respuestas se defiendan por sí mismas con respecto al razonamiento con limitaciones de segmento, arquitecturas exóticas, etc.
¿No es la simple diferencia en los nombres una razón suficiente para usar el tipo apropiado para lo correcto?
Si está almacenando una talla, úsela
size_t
. Si está almacenando un puntero, úselointptr_t
. Una persona que lea su código sabrá instantáneamente que "aha, este es un tamaño de algo, probablemente en bytes", y "oh, aquí hay un valor de puntero que se almacena como un entero, por alguna razón".De lo contrario, podría usar
unsigned long
(o, en estos tiempos modernos aquíunsigned long long
) para todo. El tamaño no lo es todo, los nombres de los tipos tienen un significado que es útil ya que ayuda a describir el programa.fuente
size_t
campo.void*
,intptr_t
yuintptr_t
están garantizados para poder representar cualquier puntero a los datos.Es posible que el tamaño de la matriz más grande sea más pequeño que un puntero. Piense en arquitecturas segmentadas: los punteros pueden ser de 32 bits, pero un solo segmento puede ser capaz de direccionar solo 64 KB (por ejemplo, la antigua arquitectura 8086 en modo real).
Si bien estos ya no se usan comúnmente en máquinas de escritorio, el estándar C está diseñado para admitir incluso arquitecturas pequeñas y especializadas. Todavía se están desarrollando sistemas integrados con CPU de 8 o 16 bits, por ejemplo.
fuente
size_t
también debería poder manejar eso. ¿O las matrices dinámicas en algún segmento lejano todavía se limitarían a la indexación dentro de su segmento?str
funciones y Borland incluso para lasmem
funciones (memset
,memcpy
,memmove
). Esto significaba que podía sobrescribir parte de la memoria cuando se desbordaba el desplazamiento, lo cual fue divertido de depurar en nuestra plataforma integrada.Me imagino (y esto se aplica a todos los nombres de tipo) que transmite mejor sus intenciones en código.
Por ejemplo, aunque
unsigned short
ywchar_t
son del mismo tamaño en Windows (creo), usar enwchar_t
lugar deunsigned short
muestra la intención de que lo use para almacenar un carácter ancho, en lugar de solo un número arbitrario.fuente
wchar_t
es mucho más grande queunsigned short
usarlo, por lo que usar uno para el otro sería erróneo y crearía un problema de portabilidad grave (y moderno), mientras que el problema de portabilidad entresize_t
yuintptr_t
parece estar en tierras lejanas de 1980-algo (puñalada al azar en la oscuridad en la fecha, allí)size_t
yuintptr_t
todavía tienen usos implícitos en sus nombres.Mirando hacia atrás y hacia adelante, y recordando que varias arquitecturas extrañas estaban dispersas por el paisaje, estoy bastante seguro de que estaban tratando de abarcar todos los sistemas existentes y también proporcionar todos los posibles sistemas futuros.
Así que, por la forma en que se resolvieron las cosas, hasta ahora no hemos necesitado tantos tipos.
Pero incluso en LP64, un paradigma bastante común, necesitábamos size_t y ssize_t para la interfaz de llamada del sistema. Uno puede imaginar un sistema heredado o futuro más restringido, en el que el uso de un tipo completo de 64 bits es costoso y es posible que deseen realizar despejes en operaciones de E / S de más de 4 GB, pero aún tienen punteros de 64 bits.
Creo que debe preguntarse: qué podría haberse desarrollado, qué podría venir en el futuro. (Tal vez punteros de todo el sistema distribuido de 128 bits en Internet, pero no más de 64 bits en una llamada al sistema, o tal vez incluso un límite "heredado" de 32 bits. :-) Imagen de que los sistemas heredados pueden obtener nuevos compiladores de C .. .
Además, mira lo que existía en ese momento. Además de los modelos de memoria en modo real zillion 286, ¿qué hay de los mainframes CDC de 60 bits de palabra / puntero de 18 bits? ¿Qué tal la serie Cray? No importa lo normal ILP64, LP64, LLP64. (Siempre pensé que Microsoft era pretencioso con LLP64, debería haber sido P64.) Ciertamente, puedo imaginar un comité tratando de cubrir todas las bases ...
fuente
Lo que implica que intptr_t siempre debe sustituir size_t y viceversa.
fuente