Sujet : Re: size_t best practice
De : andreytarasevich (at) *nospam* hotmail.com (Andrey Tarasevich)
Groupes : comp.lang.cDate : 20. Aug 2024, 14:53:54
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <va275j$3d6tp$1@dont-email.me>
References : 1
User-Agent : Mozilla Thunderbird
On 08/18/24 1:03 AM, Mark Summerfield wrote:
However, this means I have to be very careful never to decrement a size_t of
value 0, since, e.g., size_t size = 0; size--; results in size ==
18446744073709551615.
That's a completely incorrect conclusion. There's nothing wrong with decrementing a 0 of type `size_t`. It results in perfectly defined behavior. It produces a `(size_t) -1` value.
For example, iteration all the way to 0 can be idiomatically implemented as
for (some_unsigned_type i = size; (some_unsigned_type) i != -1; --i)
...
This will work, even though it will eventually decrement a zero value.
If you are sure that the type is "large" (e.g. `int` or larger), then the cast is unnecessary
for (some_unsigned_type i = size; i != -1; --i)
...
(Note, BTW, that it also works perfectly for signed index types.)
So I need to guard against this. Here is an example I'm using
(without the assert()s):
void vec_insert(vec* v, size_t index, void* value) {
if (v->_size == v->_cap) {
vec_grow(v);
}
for (size_t i = v->_size - 1; i >= index; --i) {
v->_values[i + 1] = v->_values[i];
if (!i) // if i == 0, --i will wrap!
break;
}
v->_values[index] = value;
v->_size++;
}
No, that's rather weird and unnecessarily overwrought way to guard against this.
We can immediately apply the pattern I demonstrated above to this and get
for (size_t i = v->_size - 1; i != index - 1; --i)
v->_values[i + 1] = v->_values[i];
Done. No need for an extra safeguard.
Another widely used idiom for this kind of iteration is
for (size_t i = v->size; i-- > index;)
v->_values[i + 1] = v->_values[i];
That's all. No need for any additional safeguards.
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
It is not clear what you mean here. Use signed types everywhere, including container sizes? Or use signed types just for iteration?
Anyway, the former is an iffy practice intended to replace learning with a safety blanket. The latter just leads to an unnecessary salad of types.
-- Best regards,Andrey