Me estoy confundiendo con size_t
C. Sé que lo devuelve el sizeof
operador. ¿Pero qué es exactamente? ¿Es un tipo de datos?
Digamos que tengo un for
bucle:
for(i = 0; i < some_size; i++)
¿Debo usar int i;
o size_t i;
?
De acuerdo con el estándar ISO C 1999 (C99),
size_t
es un tipo entero sin signo de al menos 16 bits (ver secciones 7.17 y 7.18.3).
size_t
es un tipo de datos sin signo definido por varios estándares C / C ++, por ejemplo, el estándar C99 ISO / IEC 9899, que se define enstddef.h
. 1 Puede importarse aún más mediante la inclusión destdlib.h
este archivo como subincluye internamentestddef.h
.Este tipo se usa 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 tamaño de operador basado en compilador más frecuentemente utilizado debe evaluarse a un valor constante que sea compatible consize_t
.
Como consecuencia, size_t
es un tipo garantizado para contener cualquier índice de matriz.
size_t
es para objetos en la memoria. El estándar C ni siquiera define stat()
o off_t
(esas son definiciones POSIX) ni nada que ver con discos o sistemas de archivos; se detiene en las FILE
transmisiones. La gestión de la memoria virtual es completamente diferente de los sistemas de archivos y la gestión de archivos en lo que respecta a los requisitos de tamaño, por lo que mencionar off_t
aquí es irrelevante.
size_t
como el tipo de resultado del sizeof
operador (7.17p2 aproximadamente <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 Wikipedia y no el estándar C real).
size_t
Es un tipo sin signo. Por lo tanto, no puede representar ningún valor negativo (<0). Lo usa cuando cuenta algo y está seguro de que no puede ser negativo. Por ejemplo, strlen()
devuelve un size_t
porque la longitud de una cadena debe ser al menos 0.
En su ejemplo, si su índice de bucle será siempre mayor que 0, podría tener sentido usar size_t
, o cualquier otro tipo de datos sin signo.
Cuando usas un size_t
objeto, debe asegurarse de que en todos los contextos que se usa, incluida la aritmética, desee valores no negativos. Por ejemplo, supongamos que tiene:
size_t s1 = strlen(str1);
size_t s2 = strlen(str2);
y quieres encontrar la diferencia de las longitudes de str2
ystr1
. Tú no puedes hacer:
int diff = s2 - s1; /* bad */
Esto se debe a que el valor asignado diff
siempre será un número positivo, incluso cuando s2 < s1
, porque el cálculo se realiza con tipos sin signo. En este caso, dependiendo de cuál sea su caso de uso, es mejor que use int
(o long long
) para 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 fgets
ideal debería ser size_t
, pero es int
.
size_t
? 2) ¿por qué debería preferir size_t
algo como unsigned int
?
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 el 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
.
size_t
se garantiza que es un tipo sin firmar.
s2 - s1
desborda un int
, el comportamiento es indefinido.
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í size_t
es como se define en stddef.h
mi máquina:
typedef unsigned long size_t;
typedef unsigned long size_t
depende del compilador. ¿O estás sugiriendo que siempre es así?
unsigned long
es de 32 bits, size_t
es de 64 bits.
size_t
no es una variable. Es un tipo que puede usar cuando desea representar el tamaño de un objeto en la memoria.
size_t
siempre hay 32 bits en una máquina de 32 bits, 64 bits también?
Si eres del tipo empírico ,
echo | gcc -E -xc -include 'stddef.h' - | grep size_t
Salida para Ubuntu 14.04 64-bit GCC 4.8:
typedef long unsigned int size_t;
Tenga en cuenta que stddef.h
es proporcionado por GCC y no glibc src/gcc/ginclude/stddef.h
en GCC 4.2.
Apariciones interesantes en C99
malloc
toma size_t
como argumento, por lo que determina el tamaño máximo que se puede asignar.
Y dado que 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?
size_t
es , al menos en una distribución popular de Linux.
Como nadie lo ha mencionado aún, el significado lingüístico principal de size_t
es que el sizeof
operador devuelve un valor de ese tipo. Del mismo modo, el significado principal de ptrdiff_t
es que restar un puntero de otro producirá un valor de ese tipo. Las funciones de biblioteca que lo aceptan lo hacen porque permitirá que tales funciones funcionen con objetos cuyo tamaño exceda UINT_MAX en sistemas donde tales objetos podrían existir, sin obligar a las personas que llaman a desperdiciar código que pasa un valor mayor que "unsigned int" en sistemas donde el tipo más grande bastaría para todos los objetos posibles.
malloc()
. En lo personal, me hubiera gustado tener versiones visto que toman argumentos de tipo int
, long
y long long
, con algunas implementaciones que promueven tipos más cortos y otros de aplicación, por ejemplo lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);}
[en algunas plataformas, llamando a imalloc(123)
sería más barato que llamar lmalloc(123);
, e incluso en una plataforma en la que size_t
es 16 bits, código que desea asignar el tamaño calculado en un valor `largo` ...
Para entrar en por qué size_t
necesitaba existir y cómo llegamos aquí:
En términos pragmáticos, size_t
y ptrdiff_t
se garantiza que tienen 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 obligar a ningún tipo existente a querer decir eso, en cada compilador, sin romper el código heredado.
A 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 a la norma en los últimos años 80, y se conviertan en obsoletos cuando C99 añadió muchos nuevos tipos pero no han ido todavía (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
puntero debía tener 32 bits de ancho, más ancho que los registros. En esos, intptr_t
habría sido de 32 bits de ancho pero size_t
yptrdiff_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 realmente.
El tipo de compensación de memoria no podría deberse a long
que demasiado código heredado supone que long
tiene exactamente 32 bits de ancho. Esta suposición incluso se incorporó a las API de UNIX y Windows. Desafortunadamente, muchos otros códigos heredados también asumieron que a long
es lo suficientemente ancho como para contener un puntero, un desplazamiento de archivo, la cantidad de segundos que han transcurrido desde 1970, y así sucesivamente. POSIX ahora proporciona una forma estandarizada de forzar que la última suposición sea verdadera en lugar de la primera, pero tampoco es una suposición portátil.
No podría ser int
porque solo un pequeño puñado de compiladores en los años 90 tenía int
64 bits de ancho. Luego se volvieron realmente raros al mantener long
32 bits de ancho. La próxima revisión del Estándar declaró ilegal int
que sea más ancho long
, pero int
aún tiene 32 bits de ancho en la mayoría de los sistemas de 64 bits.
No podría ser long long int
, lo 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 fuera así, 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 acerca de qué propiedades debía tener un tipo, y no usar uno que significara cosas diferentes en diferentes programas.
size_t
y ptrdiff_t
se garantiza que tendrá 64 bits de ancho en una implementación de 64 bits", etc. La garantía es exagerada. El rango de size_t
es impulsado principalmente por la capacidad de memoria de la implementación. "una implementación de n bits" es principalmente el ancho del procesador nativo de enteros. Ciertamente, muchas implementaciones usan una memoria de tamaño similar y un ancho de bus de procesador, pero existen enteros anchos con memoria escasa o procesadores estrechos con mucha memoria y separan estas dos propiedades de implementación.
size_t
y int
no son intercambiables Por ejemplo, en Linux de size_t
64 bits 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
no está firmado. Si necesita una versión firmada, la 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 usar size_t
/ ssize_t
cuando exista una necesidad específica ( mmap()
por ejemplo).
En general, si está comenzando en 0 y va hacia arriba, use siempre un tipo sin signo para evitar un desbordamiento que lo lleve a una situación de valor negativo. Esto es críticamente importante, porque si los límites de su matriz son menores que el máximo de su bucle, pero su max de bucle es mayor que el máximo de su tipo, se ajustará a negativo y puede experimentar una falla de segmentación (SIGSEGV ) Entonces, en general, nunca use int para un ciclo que comienza en 0 y va hacia arriba. Use un sin firmar.
size_t es un tipo de datos entero sin signo. En los sistemas que usan la Biblioteca GNU C, esto será unsigned int o unsigned long int. size_t se usa comúnmente para la indexación de matrices y el conteo de bucles.
size_t o cualquier tipo sin signo puede verse usado como variable de bucle ya que las variables de bucle son típicamente mayores o iguales a 0.
Cuando usamos un objeto size_t , debemos asegurarnos de que en todos los contextos que se usa, incluida la aritmética, solo queremos valores no negativos. Por ejemplo, el siguiente programa definitivamente daría el resultado inesperado:
// C program to demonstrate that size_t or
// any unsigned int type should be used
// carefully when used in a loop
#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];
// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;
// But reverse cycles are tricky for unsigned
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}
Output
Infinite loop and then segmentation fault
size_t
es un tipo de datos entero sin signo que puede asignar solo valores enteros 0 y mayores que 0. Mide los bytes del tamaño de cualquier objeto y los devuelve el sizeof
operador.
const
es la representación de sintaxis size_t
, pero sin const
usted puede ejecutar el programa.
const size_t number;
size_t
Se utiliza regularmente para la indexación de matrices y el conteo de bucles. Si el compilador 32-bit
funciona, funcionaría unsigned int
. Si el compilador es 64-bit
funcionaría unsigned long long int
también. Hay para el tamaño máximo de size_t
dependiendo del tipo de compilador.
size_t
ya definir el <stdio.h>
archivo de cabecera, pero también puede definir por
<stddef.h>
, <stdlib.h>
, <string.h>
, <time.h>
, <wchar.h>
cabeceras.
const
)#include <stdio.h>
int main()
{
const size_t value = 200;
size_t i;
int arr[value];
for (i = 0 ; i < value ; ++i)
{
arr[i] = i;
}
size_t size = sizeof(arr);
printf("size = %zu\n", size);
}
Salida -: size = 800
const
)#include <stdio.h>
int main()
{
size_t value = 200;
size_t i;
int arr[value];
for (i = 0 ; i < value ; ++i)
{
arr[i] = i;
}
size_t size = sizeof(arr);
printf("size = %zu\n", size);
}
Salida -: size = 800
Según tengo entendido, size_t
es un unsigned
número entero cuyo tamaño de bits es lo suficientemente grande como para contener un puntero de la arquitectura nativa.
Entonces:
sizeof(size_t) >= sizeof(void*)
size_t
. Varios ejemplos: los compiladores de C en modo real x86 pueden tener 32 bits FAR
o HUGE
punteros, pero size_t sigue siendo de 16 bits. Otro ejemplo: Watcom C solía tener un puntero gordo especial para memoria extendida que tenía 48 bits de ancho, pero size_t
no lo era. En el controlador embebido con arquitectura Harvard, tampoco tiene correlación, porque ambos se refieren a diferentes espacios de direcciones.
size_t
int
ifsome_size
está firmado,size_t
si no está firmado.int i
puede no ser suficiente para abordar una gran variedad. Entonces, al usarsize_t i
puede abordar más índices, por lo que incluso si tiene una gran matriz que no debería ser un problema.size_t
es un tipo de datos: generalmente ununsigned long int
pero esto depende de su sistema.