me estoy confundiendo con size_t
en C. Sé que es devuelto por el sizeof
operador. ¿Pero qué es exactamente? ¿Es un tipo de datos?
digamos que tengo un for
círculo:
for(i = 0; i < some_size; i++)
debo usar int i;
o size_t i;
?
Vijay
me estoy confundiendo con size_t
en C. Sé que es devuelto por el sizeof
operador. ¿Pero qué es exactamente? ¿Es un tipo de datos?
digamos que tengo un for
círculo:
for(i = 0; i < some_size; i++)
debo usar int i;
o size_t i;
?
sblom
Según la norma ISO C de 1999 (C99),
size_t
es un tipo entero sin signo de al menos 16 bits (consulte las secciones 7.17 y 7.18.3).
size_t
es un tipo de datos sin firmar definido por varios estándares C/C++, por ejemplo, el estándar C99 ISO/IEC 9899, que se define enstddef.h
.1 Se puede importar aún más mediante la inclusión de
stdlib.h
ya que este archivo incluye sub internamentestddef.h
.Este tipo se utiliza para representar el tamaño de un objeto. Las funciones de biblioteca que toman o devuelven tamaños esperan que sean de tipo o tengan el tipo de retorno de
size_t
. Además, el operador basado en compilador más utilizado, sizeof, debe evaluarse como un valor constante que sea compatible con
size_t
.
Como implicación, size_t
es un tipo garantizado para contener cualquier índice de matriz.
“Las funciones de biblioteca que toman o devuelven tamaños esperan que sean del tipo … size_t” Excepto que stat() usa off_t para el tamaño de un archivo
– Draemon
26 mayo 2010 a las 22:12
@Draemon Ese comentario refleja una confusión fundamental. size_t
es para objetos en la memoria. El estándar C ni siquiera define stat()
o off_t
(esas son definiciones POSIX) o cualquier cosa que tenga que ver con discos o sistemas de archivos – se detiene en FILE
arroyos La administración de la memoria virtual es completamente diferente de los sistemas de archivos y la administración de archivos en lo que respecta a los requisitos de tamaño, por lo que mencionar off_t
es irrelevante aquí.
– jw013
10 de junio de 2013 a las 19:57
@ jw013: Difícilmente lo llamaría una confusión fundamental, pero haces un punto interesante. Aún así, el texto citado no dice “tamaños de objetos en memoria”, y “desplazamiento” no es un buen nombre para un tipo de tamaño, independientemente de dónde esté almacenado.
– Draemon
13 de junio de 2013 a las 22:50
@Draemon Buen punto. Esta respuesta cita a Wikipedia, que en este caso no tiene la mejor explicación, en mi opinión. El estándar C en sí mismo es mucho más claro: define size_t
como el tipo del resultado de la sizeof
operador (7.17p2 sobre <stddef.h>
). La sección 6.5 explica exactamente cómo funcionan las expresiones C (6.5.3.4 para sizeof
). Como no puede aplicar sizeof
a un archivo de disco (principalmente porque C ni siquiera define cómo funcionan los discos y los archivos), no hay lugar para la confusión. En otras palabras, culpe a Wikipedia (y esta respuesta por citar a Wikipedia y no al estándar C real).
– jw013
13 de junio de 2013 a las 22:57
@Draemon: también estaría de acuerdo con la evaluación de “confusión fundamental”. Si no ha leído los estándares de C/C++, podría pensar que “objeto” se refiere a “programación orientada a objetos”, lo cual no es así. Lea el estándar C, que no tiene ninguno de esos objetos OOP, pero aún tiene objetos, y descúbralo. ¡La respuesta puede sorprenderte!
–Heath Hunnicutt
29 de octubre de 2013 a las 2:26
Alok Singhal
size_t
es un tipo sin firmar. Por lo tanto, no puede representar ningún valor negativo (<0). Lo usa cuando está contando algo y está seguro de que no puede ser negativo. Por ejemplo, strlen()
devuelve un size_t
porque la longitud de una cadena tiene que ser al menos 0.
En su ejemplo, si su índice de bucle va a ser siempre mayor que 0, podría tener sentido usar size_t
o cualquier otro tipo de datos sin firmar.
Cuando usas un size_t
objeto, debe asegurarse de que en todos los contextos en los que se usa, incluida la aritmética, desea valores no negativos. Por ejemplo, digamos que tienes:
size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
y quieres encontrar la diferencia de las longitudes de str2
y str1
. Tú no puedes hacer:
int diff = s2 - s1; /* bad */
Esto se debe a que el valor asignado a diff
siempre va a ser un número positivo, incluso cuando s2 < s1
, porque el cálculo se realiza con tipos sin firmar. En este caso, dependiendo de cuál sea su caso de uso, es mejor que use int
(o long long
) por s1
y s2
.
Hay algunas funciones en C/POSIX que podrían/deberían usar size_t
, pero no por razones históricas. Por ejemplo, el segundo parámetro a fgets
idealmente debería ser size_t
pero es int
.
@Alok: dos preguntas: 1) ¿cuál es el tamaño de size_t
? 2) por qué debería preferir size_t
sobre algo como unsigned int
?
– Lazer
8 de junio de 2010 a las 18:41
@Lazer: el tamaño de size_t
es sizeof(size_t)
. El estándar C garantiza que SIZE_MAX
será al menos 65535. size_t
es el tipo devuelto por sizeof
operador, y se utiliza en la biblioteca estándar (por ejemplo strlen
devoluciones size_t
). Como dijo Brendan, size_t
no tiene por qué ser lo mismo que unsigned int
.
– Alok Singhal
9 de junio de 2010 a las 5:56
@Lazer – sí, size_t
se garantiza que es un tipo sin firmar.
– Alok Singhal
13 de junio de 2010 a las 14:37
Tenga en cuenta que en Linux de 64 bits, int siempre es de 32 bits, pero size_t es de 64 bits. Entonces size_t e int NO son intercambiables.
– dtoux
30 de octubre de 2013 a las 2:41
@JasonOster, el complemento a dos no es un requisito en el estándar C. Si el valor de s2 - s1
desborda un int
el comportamiento no está definido.
– Alok Singhal
6 de julio de 2015 a las 0:29
Arjun Sreedharan
size_t
es un tipo que puede contener cualquier índice de matriz.
Dependiendo de la implementación, puede ser cualquiera de:
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
Así es cómo size_t
se define en stddef.h
de mi maquina:
typedef unsigned long size_t;
@chux: De hecho, solo porque una implementación lo defina como tal no significa que todos lo hagan. Caso en cuestión: Windows de 64 bits. unsigned long
es de 32 bits, size_t
es de 64 bits.
– Tim Cas
28 de diciembre de 2014 a las 21:40
¿Es cierto que size_t
¿Es siempre 32 bits en una máquina de 32 bits, 64 bits igualmente?
– Juan Wu
24 de agosto de 2016 a las 7:09
“Según el estándar ISO C de 1999 (C99), size_t es un tipo de entero sin signo de al menos 16 bits (consulte las secciones 7.17 y 7.18.3)”. Entonces no puede ser un unsigned char
?
– jamesfisher
29 noviembre 2016 a las 22:35
@jameshfisher No estoy seguro de que la restricción de 16 bits sea cierta. uint_least16_t
es lo que es al menos 16 bits. Acerca de, size_t
el estándar dice “tipo integral sin signo del resultado del operador sizeof” y “El operador sizeof produce el tamaño (en bytes) de su operando”.
– Arjun Sreedharan
30 de noviembre de 2016 a las 5:51
@jameshfisher quien dice unsigned char
no puede ser de 16 bits?!
– Antti Haapala — Слава Україні
16 de octubre de 2017 a las 7:52
Davislor
Para entrar en por qué size_t
necesario para existir y cómo llegamos aquí:
En términos pragmáticos, size_t
y ptrdiff_t
se garantiza que tengan 64 bits de ancho en una implementación de 64 bits, 32 bits de ancho en una implementación de 32 bits, y así sucesivamente. No podían forzar ningún tipo existente para que significara eso, en cada compilador, sin romper el código heredado.
UN size_t
o ptrdiff_t
no es necesariamente lo mismo que un intptr_t
o uintptr_t
. Eran diferentes en ciertas arquitecturas que todavía estaban en uso cuando size_t
y ptrdiff_t
se agregaron al estándar a fines de la década de 1980 y se volvieron obsoletos cuando C99 agregó muchos tipos nuevos pero aún no se han ido (como Windows de 16 bits). El x86 en modo protegido de 16 bits tenía una memoria segmentada donde la matriz o estructura más grande posible podía tener solo 65 536 bytes de tamaño, pero un far
el puntero debía tener 32 bits de ancho, más ancho que los registros. Sobre estos, intptr_t
habría sido de 32 bits de ancho, pero size_t
y ptrdiff_t
podría tener 16 bits de ancho y caber en un registro. ¿Y quién sabía qué tipo de sistema operativo podría escribirse en el futuro? En teoría, la arquitectura i386 ofrece un modelo de segmentación de 32 bits con punteros de 48 bits que ningún sistema operativo ha utilizado nunca.
El tipo de un desplazamiento de memoria no pudo ser long
porque demasiado código heredado asume que long
tiene exactamente 32 bits de ancho. Esta suposición incluso se incorporó en las API de UNIX y Windows. Desafortunadamente, muchos otros códigos heredados también asumieron que un long
es lo suficientemente ancho para contener un puntero, un desplazamiento de archivo, la cantidad de segundos que han transcurrido desde 1970, etc. POSIX ahora proporciona una forma estandarizada de forzar que la última suposición sea cierta en lugar de la primera, pero tampoco es una suposición portátil.
no puede ser int
porque solo un pequeño puñado de compiladores en los años 90 hizo int
64 bits de ancho. Entonces realmente se pusieron raros al mantener long
32 bits de ancho. La siguiente revisión de la Norma la declaró ilegal para int
ser más ancho que long
pero int
todavía tiene 32 bits de ancho en la mayoría de los sistemas de 64 bits.
no puede ser long long int
que de todos modos se agregó más tarde, ya que se creó para tener al menos 64 bits de ancho incluso en sistemas de 32 bits.
Entonces, se necesitaba un nuevo tipo. Incluso si no lo fuera, todos esos otros tipos significaban algo más que un desplazamiento dentro de una matriz u objeto. Y si hubo una lección del fiasco de la migración de 32 a 64 bits, fue ser específico sobre qué propiedades debe tener un tipo, y no usar uno que significa cosas diferentes en diferentes programas.
dtoux
size_t
y int
no son intercambiables. Por ejemplo, en Linux de 64 bits size_t
tiene un tamaño de 64 bits (es decir, sizeof(void*)
) pero int
es de 32 bits.
También tenga en cuenta que size_t
está sin firmar. Si necesita una versión firmada, entonces hay ssize_t
en algunas plataformas y sería más relevante para su ejemplo.
Como regla general, sugeriría usar int
para la mayoría de los casos generales y solo use size_t
/ssize_t
cuando hay una necesidad específica para ello (con mmap()
por ejemplo).
Comunidad
Si eres del tipo empírico,
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Salida para Ubuntu 14.04 GCC 4.8 de 64 bits:
typedef long unsigned int size_t;
Tenga en cuenta que stddef.h
es proporcionado por GCC y no por glibc bajo src/gcc/ginclude/stddef.h
en CCG 4.2.
Interesantes apariciones del C99
malloc
acepta size_t
como argumento, por lo que determina el tamaño máximo que se puede asignar.
Y como también es devuelto por sizeof
creo que limita el tamaño máximo de cualquier matriz.
Ver también: ¿Cuál es el tamaño máximo de una matriz en C?
coadicto
La página de manual para tipos.h dice:
size_t debe ser un tipo entero sin signo
Si esas son sus únicas opciones, use
int
Sisome_size
está firmado,size_t
si no está firmado.– Nate
31 de marzo de 2010 a las 5:59
@Nate Eso es incorrecto. POSIX tiene un tipo ssize_t pero el tipo realmente correcto para usar es ptrdiff_t.
– Molossus Spondee
19 de marzo de 2017 a las 18:46
Las respuestas no son tan claras como en Programación de bajo nivel: C, ensamblaje y ejecución de programas en Intel® 64. Como se indica en el libro, usando un índice
int i
puede no ser suficiente para abordar una gran variedad. Así que al usarsize_t i
puede abordar más índices, por lo que incluso si tiene una matriz enorme, eso no debería ser un problema.size_t
es un tipo de datos: generalmente ununsigned long int
pero esto depende de su sistema.–bruno
14 de febrero de 2020 a las 20:06