Use size_t for variables that model size or index in an array. size_t conveys semantics: you immediately know it represents a size in bytes or an index, rather than just another integer. Also, using size_t to represent a size in bytes helps making the code portable.
Its defined as unsigned integer type of the result of the sizeof operator. And C++ Standard says (about cstddef header) in §18.1/3, The contents are the same as the Standard C library header , with the following changes. So yeah, both are same; the only difference is that C++ defines size_t in std namespace.
size_t is used to store sizes of data objects, and is guaranteed to be able to hold the size of any data object that the particular C implementation can create. This data type may be smaller (in number of bits), bigger or exactly the same as unsigned int.
When writing C code you should always use size_t whenever dealing with memory ranges. The int type on the other hand is basically defined as the size of the (signed) integer value that the host machine can use to most efficiently perform integer arithmetic.
A good rule of thumb is for anything that you need to compare in the loop condition against something that is naturally a std::size_t
itself.
std::size_t
is the type of any sizeof
expression and as is guaranteed to be able to express the maximum size of any object (including any array) in C++. By extension it is also guaranteed to be big enough for any array index so it is a natural type for a loop by index over an array.
If you are just counting up to a number then it may be more natural to use either the type of the variable that holds that number or an int
or unsigned int
(if large enough) as these should be a natural size for the machine.
size_t
is the result type of the sizeof
operator.
Use size_t
for variables that model size or index in an array. size_t
conveys semantics: you immediately know it represents a size in bytes or an index, rather than just another integer.
Also, using size_t
to represent a size in bytes helps making the code portable.
The size_t
type is meant to specify the size of something so it's natural to use it, for example, getting the length of a string and then processing each character:
for (size_t i = 0, max = strlen (str); i < max; i++)
doSomethingWith (str[i]);
You do have to watch out for boundary conditions of course, since it's an unsigned type. The boundary at the top end is not usually that important since the maximum is usually large (though it is possible to get there). Most people just use an int
for that sort of thing because they rarely have structures or arrays that get big enough to exceed the capacity of that int
.
But watch out for things like:
for (size_t i = strlen (str) - 1; i >= 0; i--)
which will cause an infinite loop due to the wrapping behaviour of unsigned values (although I've seen compilers warn against this). This can also be alleviated by the (slightly harder to understand but at least immune to wrapping problems):
for (size_t i = strlen (str); i-- > 0; )
By shifting the decrement into a post-check side-effect of the continuation condition, this does the check for continuation on the value before decrement, but still uses the decremented value inside the loop (which is why the loop runs from len .. 1
rather than len-1 .. 0
).
By definition, size_t
is the result of the sizeof
operator. size_t
was created to refer to sizes.
The number of times you do something (10, in your example) is not about sizes, so why use size_t
? int
, or unsigned int
, should be ok.
Of course it is also relevant what you do with i
inside the loop. If you pass it to a function which takes an unsigned int
, for example, pick unsigned int
.
In any case, I recommend to avoid implicit type conversions. Make all type conversions explicit.
almost never
Whenever you need to have a vector of char bigger that 2gb on a 32 bit system. In every other use case, using a signed type is much safer than using an unsigned type.
example:
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]);
The signed equivalent of size_t
is ptrdiff_t
, not int
. But using int
is still much better in most cases than size_t. ptrdiff_t
is long
on 32 and 64 bit systems.
This means that you always have to convert to and from size_t whenever you interact with a std::containers, which not very beautiful. But on a going native conference the authors of c++ mentioned that designing std::vector with an unsigned size_t was a mistake.
If your compiler gives you warnings on implicit conversions from ptrdiff_t to size_t, you can make it explicit with constructor syntax:
calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);
if just want to iterate a collection, without bounds cheking, use range based for:
for(const auto& d : data) {
[...]
}
here some words from Bjarne Stroustrup (C++ author) at going native
For some people this signed/unsigned design error in the STL is reason enough, to not use the std::vector, but instead an own implementation.
size_t
is a very readable way to specify the size dimension of an item - length of a string, amount of bytes a pointer takes, etc.
It's also portable across platforms - you'll find that 64bit and 32bit both behave nicely with system functions and size_t
- something that unsigned int
might not do (e.g. when should you use unsigned long
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With