c++ - ssize_t - ¿Cuándo debería usar std:: size_t?
size_t struct (13)
Me pregunto si debería usar std::size_t
para bucles y cosas en vez de int
? Por ejemplo:
#include <cstdint>
int main()
{
for (std::size_t i = 0; i < 10; ++i) {
// std::size_t OK here? Or should I use, say, unsigned int instead?
}
}
En general, ¿cuál es la mejor práctica con respecto a cuándo usar std::size_t
?
respuesta corta:
casi nunca
respuesta larga:
Siempre que necesite tener un vector de carbón más grande que 2 gb en un sistema de 32 bits. En cualquier otro caso de uso, usar un tipo firmado es mucho más seguro que usar un tipo sin firmar.
ejemplo:
std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous
// do some bounds checking
if( i - 1 < 0 ) {
// always false, because 0-1 on unsigned creates an underflow
return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
// if i already had an underflow, this becomes true
return RIGHT_BORDER;
}
// now you have a bug that is very hard to track, because you never
// get an exception or anything anymore, to detect that you actually
// return the false border case.
return calc_something(data[i-1], data[i], data[i+1]);
El equivalente firmado de size_t
es ptrdiff_t
, no int
. Pero el uso de int
es aún mucho mejor en la mayoría de los casos que size_t. ptrdiff_t
es long
en sistemas de 32 y 64 bits.
Esto significa que siempre debe convertir hacia y desde size_t cada vez que interactúa con std :: containers, que no es muy bonito. Pero en una conferencia nativa en curso, los autores de c ++ mencionaron que diseñar std :: vector con unsigned size_t era un error.
Si su compilador le da advertencias sobre las conversiones implícitas de ptrdiff_t a size_t, puede hacerlo explícito con la sintaxis del constructor:
calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);
si solo desea iterar una colección, sin límites haciendo chequeo, use un rango basado en:
for(const auto& d : data) {
[...]
}
aquí algunas palabras de Bjarne Stroustrup (autor de C ++) al volverse nativo
Para algunas personas, este error de diseño firmado / no firmado en el STL es motivo suficiente para no usar std :: vector, sino una implementación propia.
Al usar size_t, tenga cuidado con la siguiente expresión
size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
cout << containner[i-x] << " " << containner[i+x] << endl;
}
Obtendrá false en la expresión if independientemente del valor que tenga para x. Me tomó varios días darme cuenta de esto (el código es tan simple que no hice la prueba unitaria), aunque solo lleva unos minutos encontrar la fuente del problema. No estoy seguro de si es mejor hacer un yeso o usar cero.
if ((int)(i-x) > -1 or (i-x) >= 0)
Ambas formas deberían funcionar. Aquí está mi prueba de ejecución
size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;
La salida: i-7 = 18446744073709551614 (int) (i-7) = - 2
Me gustaría los comentarios de otros.
El tipo size_t
está destinado a especificar el tamaño de algo, por lo que es natural usarlo, por ejemplo, obtener la longitud de una cadena y luego procesar cada carácter:
for (size_t i = 0, max = strlen (str); i < max; i++)
doSomethingWith (str[i]);
Por supuesto, debes tener cuidado con las condiciones de contorno, ya que es un tipo sin firmar. El límite en el extremo superior generalmente no es tan importante ya que el máximo suele ser grande (aunque es posible llegar allí). La mayoría de la gente simplemente usa un int
para ese tipo de cosas porque rara vez tienen estructuras o arreglos que crecen lo suficiente como para superar la capacidad de ese int
.
Pero ten cuidado con cosas como:
for (size_t i = strlen (str) - 1; i >= 0; i--)
que causará un ciclo infinito debido al comportamiento de ajuste de los valores sin signo (aunque he visto que los compiladores advierten contra esto). Esto también se puede aliviar con (un poco más difícil de entender pero al menos inmune a los problemas de envoltura):
for (size_t i = strlen (str); i-- > 0; )
Al desplazar la disminución a un efecto secundario posterior a la verificación de la condición de continuación, se realiza la comprobación de continuación del valor antes de la disminución, pero todavía se utiliza el valor decrementado dentro del ciclo (razón por la cual el ciclo se ejecuta desde la posición len .. 1
en lugar de len-1 .. 0
).
Por definición, size_t
es el resultado del operador sizeof
. size_t
fue creado para referirse a los tamaños.
La cantidad de veces que haces algo (10, en tu ejemplo) no se trata de tamaños, entonces, ¿por qué usar size_t
? int
, o unsigned int
, debería estar bien.
Por supuesto, también es relevante lo que haces con i
dentro del ciclo. Si pasa a una función que toma un unsigned int
, por ejemplo, elija unsigned int
.
En cualquier caso, recomiendo evitar las conversiones de tipo implícitas. Haga que todas las conversiones de tipo sean explícitas.
Pronto, la mayoría de las computadoras serán arquitecturas de 64 bits con sistema operativo de 64 bits: se ejecutan programas que operan en contenedores de miles de millones de elementos. Luego debe usar size_t
lugar de int
como índice de bucle, de lo contrario su índice se ajustará al elemento 2 ^ 32: th, en los sistemas de 32 y 64 bits.
Prepárate para el futuro
Una buena regla empírica es para cualquier cosa que necesite comparar en la condición de bucle contra algo que es naturalmente un std::size_t
sí mismo.
std::size_t
es el tipo de cualquier tamaño de expresión y se garantiza que podrá expresar el tamaño máximo de cualquier objeto (incluida cualquier matriz) en C ++. Por extensión, también se garantiza que es lo suficientemente grande para cualquier índice de matriz, por lo que es un tipo natural para un bucle por índice en una matriz.
Si solo está contando hasta un número, puede ser más natural usar el tipo de la variable que contiene ese número o una int
o unsigned int
(si es lo suficientemente grande) ya que estos deben ser de un tamaño natural para la máquina.
Utilice std :: size_t para indexar / contar matrices estilo C.
Para contenedores STL, tendrá (por ejemplo) vector<int>::size_type
, que se debe usar para indexar y contar elementos vectoriales.
En la práctica, generalmente son dos entradas sin firmar, pero no está garantizado, especialmente cuando se usan asignadores personalizados.
size_t es devuelto por varias bibliotecas para indicar que el tamaño de ese contenedor no es cero. Lo usas cuando vuelves una vez: 0
Sin embargo, en el ejemplo anterior, el bucle en un tamaño_t es un posible error. Considera lo siguiente:
for (size_t i = thing.size(); i >= 0; --i) {
// this will never terminate because size_t is a typedef for
// unsigned int which can not be negative by definition
// therefore i will always be >= 0
printf("the never ending story. la la la la");
}
el uso de enteros sin signo tiene el potencial de crear estos tipos de problemas sutiles. Por lo tanto, prefiero usar size_t solo cuando interactúo con los contenedores / tipos que lo requieren.
size_t es un tipo integral sin signo, que puede representar el entero más grande en su sistema. Úselo solo si necesita matrices, matrices, etc. muy grandes.
Algunas funciones devuelven un size_t y su compilador le advertirá si intenta hacer comparaciones.
Evite eso usando un tipo de datos firmado / no firmado apropiado o simplemente encasillado para un hack rápido.
size_t no tiene firma int. así que siempre que quieras int sin firmar puedes usarlo.
Lo uso cuando quiero especificar el tamaño de la matriz, contador ect ...
void * operator new (size_t size); is a good use of it.
size_t
es el tipo de resultado del operador sizeof
.
Use size_t
para las variables que modelan tamaño o índice en una matriz. size_t
transmite semántica: inmediatamente sabe que representa un tamaño en bytes o un índice, en lugar de solo otro entero.
Además, usar size_t
para representar un tamaño en bytes ayuda a que el código sea portátil.
size_t
es un tipo sin signo que puede contener el valor entero máximo para su arquitectura, por lo que está protegido de desbordamientos enteros debido al signo (con la firma int 0x7FFFFFFF
incrementado en 1 le dará -1) o el tamaño corto (sin signo corto int 0xFFFF incrementado en 1 darte 0).
Se utiliza principalmente en la indexación / bucles / aritmética de direcciones, etc. Las funciones como memset()
y similares aceptan solo size_t
, porque teóricamente puede tener un bloque de memoria de tamaño 2^32-1
(en la plataforma de 32 bits).
Para esos bucles simples, no te molestes y utiliza simplemente int.
size_t
es una forma muy legible de especificar la dimensión de tamaño de un elemento: longitud de una cadena, cantidad de bytes que toma un puntero, etc. También es portátil en todas las plataformas: encontrará que tanto 64 bits como 32 bits se comportan bien con las funciones del sistema y size_t
- algo que unsigned int
podría no funcionar (por ejemplo, cuando debería usar unsigned long