I've got an impression that size_t
is unsigned int
. But can I just write unsigned int
instead of size_t
in my code?
-
10This is plainly false on a great many systems. For instance on my computer, `unsigned int` is 32 bits and `size_t` is 64 bits. – Nate Eldredge Mar 16 '21 at 15:23
-
1Please always tag your question with the programming language you are asking about. I've added the [tag:c] tag, but if you're actually asking about C++ or something else, please edit the tags accordingly. – Nate Eldredge Mar 16 '21 at 15:24
-
1@NateEldredge This is the one case I'd add both C and C++ – Casey Mar 16 '21 at 15:39
-
1C++ `std::size_t` is defined in `
`, ` – Eljay Mar 16 '21 at 15:49`, ` `, ` `, ` `, ` `, or ` `. It is effectively `using size_t = decltype(sizeof 0);`. It may be identical to (and I'm not sure if this is exhaustive): `unsigned int`, `unsigned long`, or `unsigned long long` depending on your platform. -
@Casey That was wrong, you should not add or change language tags when there are already answers present for one language or the other. We also have explicit rules for how to cross tag C and C++ questions. The rule of thumb is, in case a question doesn't not contain any trace of C++ nor any remarks about getting compiled with a C++ compiler, it should not have the C++ tag. Read the C and C++ tag wikis for details. I'll rollback. – Lundin Mar 16 '21 at 16:33
2 Answers
size_t
is the most correct type to use when describing the sizes of arrays and objects. It's guaranteed to be unsigned and is supposedly "large enough" to hold any object size for the given system. Therefore it is more portable to use for that purpose than unsigned int
, which is in practice either 16 or 32 bits on all common computers.
So the most canonical form of a for
loop when iterating over an array is actually:
for(size_t i=0; i<sizeof array/sizeof *array; i++)
{
do_something(array[i]);
}
And not int i=0;
which is perhaps more commonly seen even in some C books.
size_t
is also the type returned from the sizeof
operator. Using the right type might matter in some situations, for example printf("%u", sizeof obj);
is formally undefined behavior, so it might in theory crash printf or print gibberish. You have to use %zu
for size_t
.
It is quite possible that size_t
happens to be the very same type as unsigned long
or unsigned long long
or uint32_t
or uint64_t
though.

- 22,760
- 4
- 24
- 39

- 195,001
- 40
- 254
- 396
-
2@JDługosz This was originally tagged C when I answered the question. If the question has been vandalized since then, that's no fault of mine. – Lundin Mar 16 '21 at 16:31
-
1@JDługosz Out of curiousity though, why would you _not_ write loops as simple and readable as they come in C++? You can't really out-of-bounds access this array by accident unless you meddle with the array index. There's no obvious benefit of using iterators for plain old data types either. See, we must not write simple code complex for the sake of it, or for the sake of cramming as many post C++11 features as possible into the same translation unit... – Lundin Mar 16 '21 at 16:37
-
Yes, I'd write it as simple and readable as possible. Normally: `for (auto& x : array) do_something(x);`. If you need the subscript for some (good) reason, and don't have a good library thing for that handy in your project (e.g. `for (auto i : iota(array.size()))` , use a good way to get the array bounds, not a nasty brittle way. – JDługosz Mar 16 '21 at 17:23
-
@JDługosz Except `auto` is nasty and brittle... because it has no type safety. If you happen to pass the wrong type to it by accident, your subtle typing bugs spread further through the project and you can no longer tell what types your variables are, so tracking down such bugs is very hard. – Lundin Mar 17 '21 at 08:02
-
I find just the opposite. Writing code as if it were generic makes it automatically adapt when other code changes, reducing maintenance, prevents unnecessary conversions when people type a type that's not quite right, and makes it easy to turn into a template later if we want to generalize the code further. I don't know what you mean by "subtle typing bugs spread" -- it _works_ with the type given. If you have a cautionary tale, I'd like to know, and wonder what a good place to post it would be. – JDługosz Mar 17 '21 at 13:24
-
@JDługosz The main issue is the dysfunctional implicit type promotion rules of C++. Quite often you need to be very explicit with which type you use, particularly when working with bitwise operators. Something like `auto bug = my_uint8 + 1;` gives you a signed int where you needed an unsigned byte. Unlike `uint8_t correct = my_uint8 + 1;`. If you then go ahead and do something like `bug << n` or write code with wrap-around, you could invoke UB. These well-known flaws of the language shouldn't have come as a surprise to the C++11 committee, since these problems are inherited from C. – Lundin Mar 18 '21 at 09:06
-
`uint8_t correct { my_uint8 + 1 };` even better will give an error due to narrowing. The new form disallows narrowing conversions. I see your point on conversions in arithmetic. That's not going to happen when doing object manipulation instead -- accessors and other member functions, navigating connections of related objects, etc. – JDługosz Mar 18 '21 at 15:17
-
@JDługosz Well... instead of coming up with all of these new, poorly considered features, they should simply fix implicit type promotions. It's one of the most broken and dangerous parts of C and C++. – Lundin Mar 18 '21 at 15:57
-
With an established language, there's an issue with "breaking changes". – JDługosz Mar 19 '21 at 14:02
-
@JDługosz Yes, such as changing the behavior of `auto`... you could have old C89 code with implicit int `auto correct = my_uint8 + 1;`. C++11 will cause that code to bug out into the wilds. – Lundin Mar 19 '21 at 14:07
-
The change to `auto` (rather than using a new keyword or syntax) was carefully considered. Large surveys of existing C++ code were checked for any uses of `auto`, to see if it was actually used anywhere (it wasn't). Compilers were updated to issue a warning, for years, before the new behavior was actually provided. – JDługosz Mar 19 '21 at 14:15
-
@JDługosz Yeah because simply refraining from introducing dangerous new features is not an option. Maybe they could have spent their time better by checking how much C++ code there is which contains implicit type promotions in the initializer list. – Lundin Mar 19 '21 at 14:32
The type size_t
is an unsigned type capable of representing all possible results of sizeof
and _Alignof
operators.
See https://port70.net/~nsz/c/c11/n1570.html#6.5.3.4p5
The unsigned int
is an unsigned variant of ... int
that can represent integers from 0 to 65535. It may be capable of representing larger integers.
See https://port70.net/~nsz/c/c11/n1570.html#5.2.4.2.1
They may or may not be the same type. On modern 64-bit machines they are not.

- 13,520
- 2
- 25
- 40
-
1[0, 65535] is just the minimum range required for `unsigned int`. Most modern systems have bigger `unsigned int` – phuclv Mar 16 '21 at 15:44
-
-
The C standard does not explicitly guarantee that `size_t` can represent all possible results of `sizeof`, just that it is the type used for results of `sizeof`. Just as `123456*123456*123456` can overflow `int`, `sizeof(char [123456][123456][123456])` can overflow `size_t` (overflow in the mathematical sense, notwithstanding C’s wrapping for unsigned types). – Eric Postpischil Mar 16 '21 at 16:48
-
@EricPostpischil, as I understand the standard, the result of of sizeof is of size_t type. So technically , it is sizeof that is overflowed not size_t. – tstanisl Mar 16 '21 at 17:29