¿Qué es size_t en C?

626

Me estoy confundiendo con size_tC. Sé que lo devuelve el sizeofoperador. ¿Pero qué es exactamente? ¿Es un tipo de datos?

Digamos que tengo un forbucle:

for(i = 0; i < some_size; i++)

¿Debo usar int i;o size_t i;?

Vijay
fuente
11
Si esas son sus únicas opciones, use intif some_sizeestá firmado, size_tsi no está firmado.
Nate
8
@Nate Eso es incorrecto. POSIX tiene un tipo ssize_t pero el tipo realmente correcto para usar es ptrdiff_t.
Steven Stewart-Gallus
2
Las respuestas no son tan claras como en la Programación de bajo nivel: C, ensamblaje y ejecución de programas en Intel® 64 . Como se indica en el libro, el uso de un índice int ipuede no ser suficiente para abordar una gran variedad. Entonces, al usar size_t ipuede abordar más índices, por lo que incluso si tiene una gran matriz que no debería ser un problema. size_tes un tipo de datos: generalmente un unsigned long intpero esto depende de su sistema.
bruno

Respuestas:

461

De Wikipedia :

De acuerdo con el estándar ISO C 1999 (C99), size_tes un tipo entero sin signo de al menos 16 bits (ver secciones 7.17 y 7.18.3).

size_tes 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 en stddef.h. 1 Puede importarse aún más mediante la inclusión de stdlib.heste archivo como subincluye internamente stddef.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 con size_t.

Como consecuencia, size_tes un tipo garantizado para contener cualquier índice de matriz.

sblom
fuente
44
"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
64
@Draemon Ese comentario refleja una confusión fundamental. size_tes 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 FILEtransmisiones. 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_taquí es irrelevante.
jw013
3
@ jw013: difícilmente lo llamaría una confusión fundamental, pero usted hace 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
30
@Draemon Buen punto. Esta respuesta cita 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: se define size_tcomo el tipo de resultado del sizeofoperador (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 sizeofa 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).
jw013
2
@Draemon: también estaría de acuerdo con la evaluación de "confusión fundamental". Si no ha leído los estándares 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
220

size_tEs 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_tporque 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 str2ystr1 . Tú no puedes hacer:

int diff = s2 - s1; /* bad */

Esto se debe a que el valor asignado diffsiempre 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 s1y 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 fgetsideal debería ser size_t, pero es int.

Alok Singhal
fuente
8
@Alok: Dos preguntas: 1) ¿de qué tamaño es size_t? 2) ¿por qué debería preferir size_talgo como unsigned int?
Lazer
2
@Lazer: el tamaño de size_tes sizeof(size_t). El estándar C garantiza que SIZE_MAXserá al menos 65535. size_tes el tipo devuelto por el sizeofoperador y se utiliza en la biblioteca estándar (por ejemplo, strlendevoluciones size_t). Como dijo Brendan, size_tno tiene por qué ser lo mismo que unsigned int.
Alok Singhal
44
@Lazer: sí, size_tse garantiza que es un tipo sin firmar.
Alok Singhal
2
@ Celeritas no, quiero decir que un tipo sin signo solo puede representar valores no negativos. Probablemente debería haber dicho "No puede representar valores negativos".
Alok Singhal
44
@JasonOster, el complemento a dos no es un requisito en el estándar C. Si el valor de s2 - s1desborda un int, el comportamiento es indefinido.
Alok Singhal
73

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_tes como se define en stddef.hmi máquina:

typedef unsigned long size_t;
Arjun Sreedharan
fuente
44
Ciertamente typedef unsigned long size_tdepende del compilador. ¿O estás sugiriendo que siempre es así?
chux - Restablece a Monica el
44
@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 longes de 32 bits, size_tes de 64 bits.
Tim Čas
2
¿Cuál es el propósito de size_t exactamente? Cuando puedo crear una variable para mí como: "int mysize_t;" o "long mysize_t" o "unsigned long mysize_t". ¿Por qué alguien debería haber creado esta variable para mí?
midkin
1
@midkin size_tno es una variable. Es un tipo que puede usar cuando desea representar el tamaño de un objeto en la memoria.
Arjun Sreedharan
1
¿Es cierto que size_tsiempre hay 32 bits en una máquina de 32 bits, 64 bits también?
John Wu
70

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.hes proporcionado por GCC y no glibc src/gcc/ginclude/stddef.hen GCC 4.2.

Apariciones interesantes en C99

  • malloctoma size_tcomo 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?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
1
Tengo el mismo entorno, sin embargo, lo probé durante 32 bits, pasando la opción "-m32" de GCC, el resultado fue: "typedef unsigned int size_t". Gracias por compartir este increíble comando @Ciro, ¡me ayudó mucho! :-)
silvioprog
2
El asunto en sí no es confuso. Es la mente confusa que intenta hacer muchas preguntas y dar muchas respuestas. Me sorprende que esta respuesta y la de Arjun Sreedharan todavía no impidan que la gente pregunte y responda.
biocyberman
1
Gran respuesta, porque en realidad te dice qué size_tes , al menos en una distribución popular de Linux.
Andrey Portnoy
25

La página de manual para types.h dice:

size_t será un tipo entero sin signo

codictorio
fuente
19

Como nadie lo ha mencionado aún, el significado lingüístico principal de size_tes que el sizeofoperador devuelve un valor de ese tipo. Del mismo modo, el significado principal de ptrdiff_tes 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.

Super gato
fuente
Mi pregunta siempre ha sido: si sizeof nunca existió, ¿habría necesidad de size_t?
Dean P
@DeanP: Quizás no, aunque entonces habría una pregunta sobre qué tipo de argumento debería usarse para cosas como malloc(). En lo personal, me hubiera gustado tener versiones visto que toman argumentos de tipo int, longy 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_tes 16 bits, código que desea asignar el tamaño calculado en un valor `largo` ...
supercat
... debería poder confiar en que la asignación falla si el valor es mayor de lo que el asignador puede manejar.
supercat
11

Para entrar en por qué size_tnecesitaba existir y cómo llegamos aquí:

En términos pragmáticos, size_ty ptrdiff_tse 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_to ptrdiff_tno es necesariamente lo mismo que un intptr_to uintptr_t. Eran diferentes en ciertas arquitecturas que todavía estaban en uso cuando size_ty ptrdiff_tse 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 farpuntero debía tener 32 bits de ancho, más ancho que los registros. En esos, intptr_thabría sido de 32 bits de ancho pero size_typtrdiff_tpodrí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 longque demasiado código heredado supone que longtiene 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 longes 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 intporque solo un pequeño puñado de compiladores en los años 90 tenía int64 bits de ancho. Luego se volvieron realmente raros al mantener long32 bits de ancho. La próxima revisión del Estándar declaró ilegal intque sea más ancho long, pero intaú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.

Davislor
fuente
No está de acuerdo con " size_ty ptrdiff_tse garantiza que tendrá 64 bits de ancho en una implementación de 64 bits", etc. La garantía es exagerada. El rango de size_tes 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.
chux - Restablece a Mónica el
8

size_ty intno son intercambiables Por ejemplo, en Linux de size_t64 bits tiene un tamaño de 64 bits (es decir sizeof(void*)) pero intes de 32 bits.

También tenga en cuenta que size_tno está firmado. Si necesita una versión firmada, la hay ssize_ten algunas plataformas y sería más relevante para su ejemplo.

Como regla general, sugeriría usar intpara la mayoría de los casos generales y solo usar size_t/ ssize_tcuando exista una necesidad específica ( mmap()por ejemplo).

dtoux
fuente
3

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.

marca
fuente
3
No puedo aceptar tu argumentación. ¿Dice que es mejor que el error de desbordamiento silenciosamente conduzca a acceder a datos válidos dentro de su matriz?
maf-soft
1
@ maf-soft es correcto. Si el error no se detecta, es peor que un bloqueo del programa. ¿Por qué esta respuesta recibió votos positivos?
yoyo_fun
Si accede a datos válidos en su matriz, entonces no es un error porque el tipo sin signo no se desbordará en el límite con el tipo con signo. ¿Qué es esta lógica chicos? Digamos por alguna razón que usas char para iterar sobre una matriz de 256 elementos ... firmado se desbordará en 127 y el elemento 128 sigsegv, pero si usas unsigned, pasará por toda la matriz como se esperaba. Por otra parte, cuando está utilizando un int, sus matrices no serán realmente más grandes que 2 mil millones de elementos, por lo que de cualquier manera, no importa ...
Purple Ice
1
No puedo imaginar ninguna situación en la que el desbordamiento de enteros no sea un error, ya sea que sea positivo o negativo. ¡El hecho de que no obtenga un segfault no significa que vea un comportamiento correcto! Y puede experimentar una falla de segmentación, o no, si su desplazamiento es positivo o negativo; Todo depende de su diseño de memoria. @PurpleIce, no creo que estés diciendo lo mismo que esta respuesta; Su argumento parece ser que debe elegir un tipo de datos lo suficientemente grande como para contener el valor más grande que desea poner en él, lo cual es simplemente sentido común.
Soren Bjornstad
Dicho esto, prefiero usar un tipo sin signo para los índices de bucle semánticamente ; si su variable nunca va a ser negativa, entonces también podría indicarlo en el tipo que elija. También podría permitir que el compilador detecte un error donde el valor terminó siendo negativo, aunque GCC al menos es bastante terrible al detectar este error en particular (en una ocasión inicialicé un sin signo a -1 y no recibí una advertencia). Del mismo modo, un size_t es semánticamente apropiado para los índices de matriz.
Soren Bjornstad
3

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.

Príncipe
fuente
1

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
bishwas pokharel
fuente
1

size_tes 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 sizeofoperador. constes la representación de sintaxis size_t, pero sin constusted puede ejecutar el programa.

const size_t number;

size_tSe utiliza regularmente para la indexación de matrices y el conteo de bucles. Si el compilador 32-bitfunciona, funcionaría unsigned int. Si el compilador es 64-bitfuncionaría unsigned long long inttambién. Hay para el tamaño máximo de size_tdependiendo del tipo de compilador.

size_tya definir el <stdio.h>archivo de cabecera, pero también puede definir por <stddef.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>cabeceras.

  • Ejemplo (con 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


  • Ejemplo (sin 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

Kalana
fuente
-3

Según tengo entendido, size_tes un unsignednú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*)
David Zechiel
fuente
16
No es verdad. El tamaño del puntero puede ser mayor que el size_t. Varios ejemplos: los compiladores de C en modo real x86 pueden tener 32 bits FARo HUGEpunteros, 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_tno lo era. En el controlador embebido con arquitectura Harvard, tampoco tiene correlación, porque ambos se refieren a diferentes espacios de direcciones.
Patrick Schlüter
1
Y en ese stackoverflow.com/questions/1572099/… hay más ejemplos AS / 400 con punteros de 128 bits y 32 bitssize_t
Patrick Schlüter
Esto es descaradamente falso. Sin embargo, vamos a mantenerlo aquí
Antti Haapala