About size_t
and its pitfalls
What is size_t? (When to use it and the
pitfalls to watch out for when using it.)
As the name is meant to suggest (had they followed my style guide,
it'd probably be size_type
and not size_t), size_t represents the
most
suitable type that one can use to declare variables/constants for sizing
and counting things when coding in C/C++. The most suitable type
is one that will use the minimum amount of memory (maximum efficiency)
and still be able to accommodate the entire range of values that is
likely to be encountered (maximum desired flexibility). For sizing
and counting, only nonnegative values are involved. Therefore
the most suitable C/C++ built-in type for the purpose is either unsigned
int or unsigned long int. The question is which
of the two should we use. Since an unsigned long int uses
at
least the same amount of memory as an
unsigned int,
but likely more, we would want to use it only if we really need
it (for maximum efficiency). Whether we really need it or not is in turn
determined by the range of values that is likely to be encountered (the
maximum desired flexibility), which unfortunately is platform dependent.
It would be a lot of pain for the programmer to have to figure out which
of the two (unsigned int or
unsigned long int)
is most suitable for a particular platform, and to have to figure it out
again (and incorporate any necessary adjustments) each time there is a
need to port a program from one platform to another. To relieve programmers
of this burden, the compiler writers agreed to follow an established rule,
which is for them (the compiler writers) to figure out whether it is unsigned
int or unsigned long int that is the most suitable
size type for a particular platform that they are writing the compiler
for. They will then define a symbolic type name called size_t
in the cstdlib header file to be this most suitable size
type. This way, and provided that the cstdlib header file
is included, programmers can simply use size_t to represent
the most suitable size type when they need it for sizing and counting pusposes
in their code, without having to worry about the issue of platform dependence.
PITFALL: Because size_t is an unsigned
type (recall that it is either unsigned int or unsigned
long int), we must be careful not to use
a variable of type size_t in such a way that the variable
is required to store a value below zero. This may at first seem
like a trivial one that is not worth noting, but believe me many past students
fell into it and wasted many frustrating hours trying to figure out what
went wrong. To help you appreciate this, let's consider a rather simple
program that is to count down and display values from START_VALUE
to 0, where START_VALUE is some nonzero positive number.
Try compiling and running the following program:
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
const size_t START_VALUE = 10;
size_t i;
for (i = START_VALUE; i >= 0; i--)
cout << i << endl;
return EXIT_SUCCESS;
}
Does the program work as intended? If not, can you see why? Do you know
how to correct it (click here
for answer)?
PITFALL: Another size_t related pitfall occurs
only on some strict compilers. It is illustrated by the following example:
#include <iostream>
#include <cstdlib>
using namespace std;
template <class T>
void EchoManyTimes(T value, size_t numOfTimes);
int main()
{
const int J = 3;
const size_t K = 4;
int i = 2;
double d = 3.3;
char c = 'a';
EchoManyTimes(1, 1);
EchoManyTimes(i, i);
EchoManyTimes(d, J);
EchoManyTimes(c, K);
return EXIT_SUCCESS;
}
template <class T>
void EchoManyTimes(T value, size_t numOfTimes)
{
size_t count;
for (count = 1; count <= numOfTimes; count++)
cout << value <<
endl;
}
This program fails on strict compilers because such compilers require
that the arguments of a template function have an exact match, with
no
type conversion. Click here
to see how this problem can be overcome/avoided.