Discussion:
size_t best practice
(too old to reply)
Mark Summerfield
2024-08-18 08:03:08 UTC
Permalink
Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.

In view of this I'm using size_t throughout my code for array sizes and
indexes.

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.

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++;
}

I've also noticed that quite a few array-related algorithms _assume_ that
indexes are signed, so again I have to put in guards to avoid subtracting
below zero when I use size_t when implementing them.

So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
Ike Naar
2024-08-18 08:38:13 UTC
Permalink
Post by Mark Summerfield
Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.
In view of this I'm using size_t throughout my code for array sizes and
indexes.
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.
So I need to guard against this. Here is an example I'm using
[...]
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;
}
Substitution: j = i + 1

for (size_t j = v->_size; j > index; --j) {
v->_values[j] = v->_values[j - 1];
}
Mark Summerfield
2024-08-18 10:15:52 UTC
Permalink
On Sun, 18 Aug 2024 08:38:13 -0000 (UTC), Ike Naar wrote:
[snip]
Post by Ike Naar
Substitution: j = i + 1
for (size_t j = v->_size; j > index; --j) {
v->_values[j] = v->_values[j - 1];
}
Thanks, that works great.

And as per "Michael S" I checked the man pages:

ptrdiff_t Used for a count of elements, and array indices. C99 and POSIX.1-2001.

size_t Used for a count of bytes. C99 and POSIX.1-2001.

ssize_t Used for a count of bytes or an error indication. POSIX.1-2001.

So for my arrays and loops I'll change to ptrdiff_t.
Andrey Tarasevich
2024-08-20 14:38:35 UTC
Permalink
Post by Mark Summerfield
So for my arrays and loops I'll change to ptrdiff_t.
... which would be the _worst_ practice in the given context. And the
question was intended to be about the best ones ¯\_(ツ)_/¯
--
Best regards,
Andrey
Michael S
2024-08-18 09:36:49 UTC
Permalink
On Sun, 18 Aug 2024 08:03:08 +0000
Post by Mark Summerfield
Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.
In view of this I'm using size_t throughout my code for array sizes
and indexes.
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.
So I need to guard against this. Here is an example I'm using
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++;
}
I've also noticed that quite a few array-related algorithms _assume_
that indexes are signed, so again I have to put in guards to avoid
subtracting below zero when I use size_t when implementing them.
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
My personal view is that in order to minimize a surprise factor one
should prefer signed array indices and signed loop control variables.
I.e. either int or ptrdiff_t.
I wouldn't use long or long long.
Some systems have ssize_t, but that's not part of standard C. Beyond, I
can't imagine a situation where ssize_t is better than ptrdiff_t.
Tim Rentsch
2024-08-18 11:32:40 UTC
Permalink
Mark Summerfield <***@qtrac.eu> writes:

[what type to use for array indexing?]
Post by Mark Summerfield
I've also noticed that quite a few array-related algorithms
_assume_ that indexes are signed, so again I have to put in
guards to avoid subtracting below zero when I use size_t when
implementing them.
So is it considered best practice to use int, long, long long,
or size_t, in situations like these?
There is no single "best practice". Some people are firmly of
the opinion that signed types should be used everywhere. Other
people are firmly of the opinion that unsigned types should be
used for variables whose domain can never be negative, such as
counts and array indices. There is no single answer that will
get agreement from everyone.

Having said that, I have two recommendations.

One, for the particular case of array indices, use a typedef
to define a type name to be used exclusively for the purpose
of indexing arrays. My own practice is to use 'Index' for
this type, but what name is used is less important than that
there be one (following local naming conventions, etc).

Two, write code so that any use of such types will work whether
the underlying types are signed or unsigned. In other words
don't rely on either signed semantics or on unsigned semantics
for uses of those variables. Following this precept may need
more mental effort than using a type of known signedness, but
it provides a collateral benefit in that it's easier, in many
cases, to verify the correctness of the code written. That
benefit makes it worth the effort.
Stefan Ram
2024-08-18 12:17:36 UTC
Permalink
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C and
just cherry-pick this one gnarly feature from that other language.

SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
Michael S
2024-08-18 12:40:13 UTC
Permalink
On 18 Aug 2024 12:17:36 GMT
Post by Stefan Ram
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Tim Rentsch
2024-08-18 22:23:58 UTC
Permalink
Post by Michael S
On 18 Aug 2024 12:17:36 GMT
Post by Stefan Ram
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Kind of a funny use of the word intuitive, for two reasons.

The first is that writing for() loops, especially C for() loops,
is learned behavior. There can be patterns that one is used to,
but they are not "intuitive" in the usual sense of the word.

The second is that people who program in C are accustomed to the
idea of an asymmetry between counting up and counting down,
because of how pointers work. It's okay to increment a pointer
to one past the end of an array; it is not okay to decrement a
pointer to one before the beginning of an array. Because of that
the patterns for going forward and for going backward are just
different. It seems odd to use the word "intuitive" to recognize
that distinction.

Which is not to say I disagree with what you are saying. Actually
I guess I'd have to say I'm not sure what it is you are saying.
To my way of thinking the function above doesn't change the way I
would write down-counting loops. It might be useful as a debugging
aid aide, but nothing more (and an assert() is probably better).
I think though that what you're saying is something else but I'm
not sure what it is.
Michael S
2024-08-19 08:13:03 UTC
Permalink
On Sun, 18 Aug 2024 15:23:58 -0700
Post by Tim Rentsch
Post by Michael S
On 18 Aug 2024 12:17:36 GMT
Post by Stefan Ram
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Kind of a funny use of the word intuitive, for two reasons.
The first is that writing for() loops, especially C for() loops,
is learned behavior. There can be patterns that one is used to,
but they are not "intuitive" in the usual sense of the word.
The second is that people who program in C are accustomed to the
idea of an asymmetry between counting up and counting down,
because of how pointers work. It's okay to increment a pointer
to one past the end of an array; it is not okay to decrement a
pointer to one before the beginning of an array. Because of that
the patterns for going forward and for going backward are just
different. It seems odd to use the word "intuitive" to recognize
that distinction.
I would think that very large part of 'C' programmers ignores this
asymmetry. They are helped by the fact that 100% of production 'C'
compilers ignore it as well, which means that in practice code that
compares &arr[0] with &arr[-1] works as naively expected on all targets
that have flat memory model and far more often than not works on more
tricky targets as well.
Post by Tim Rentsch
Which is not to say I disagree with what you are saying. Actually
I guess I'd have to say I'm not sure what it is you are saying.
To my way of thinking the function above doesn't change the way I
would write down-counting loops. It might be useful as a debugging
aid aide, but nothing more (and an assert() is probably better).
I think though that what you're saying is something else but I'm
not sure what it is.
Nothing fancy. Just an ability to write downcounting loops in a
way that I, obviously mistakenly, consider intuitive.
for (i = len-1; i >= 0; --i)
Tim Rentsch
2024-08-19 16:43:38 UTC
Permalink
Post by Michael S
On Sun, 18 Aug 2024 15:23:58 -0700
Post by Tim Rentsch
Post by Michael S
On 18 Aug 2024 12:17:36 GMT
Post by Stefan Ram
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or
size_t, in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C
and just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
But that's not a desired behavior for people that want to write
downcounting for() loops in intuitive manner.
Kind of a funny use of the word intuitive, for two reasons.
The first is that writing for() loops, especially C for() loops,
is learned behavior. There can be patterns that one is used to,
but they are not "intuitive" in the usual sense of the word.
The second is that people who program in C are accustomed to the
idea of an asymmetry between counting up and counting down,
because of how pointers work. It's okay to increment a pointer
to one past the end of an array; it is not okay to decrement a
pointer to one before the beginning of an array. Because of that
the patterns for going forward and for going backward are just
different. It seems odd to use the word "intuitive" to recognize
that distinction.
I would think that very large part of 'C' programmers ignores this
asymmetry. They are helped by the fact that 100% of production 'C'
compilers ignore it as well, which means that in practice code that
compares &arr[0] with &arr[-1] works as naively expected on all targets
that have flat memory model and far more often than not works on more
tricky targets as well.
I long ago internalized the rule that C allows pointing one past the
end of an array but does not allow pointing before the beginning.
Certainly I don't expect that all C programmers respect that rule,
but I do expect that most experienced C programmers will, especially
given recent trends in aggressive optimizing, since the consequences
of not doing so can be so dire.

I also long ago developed the habit of not thinking about, at least
most of the time, what is going on "under the hood" when compiling.
A compiler is allowed to take advantage of how pointer arithmetic
works, precisely because the compiler has privileged access to
inside information. (It occurs to me now that this attitude might
be humorously stated: Thinking "under the hood" Considered Harmful.)

I don't think of either of those traits as ubiquitous, but I also
don't think of them as special or unusual. Maybe that assumption
isn't as generally apt as I think it is.
Post by Michael S
Post by Tim Rentsch
Which is not to say I disagree with what you are saying. Actually
I guess I'd have to say I'm not sure what it is you are saying.
To my way of thinking the function above doesn't change the way I
would write down-counting loops. It might be useful as a debugging
aid aide, but nothing more (and an assert() is probably better).
I think though that what you're saying is something else but I'm
not sure what it is.
Nothing fancy. Just an ability to write downcounting loops in a
way that I, obviously mistakenly, consider intuitive.
for (i = len-1; i >= 0; --i)
I wouldn't say mistaken just because you used what might be a funny
word choice (and not everyone would agree on that point). For
example if you had used the phrase "in a natural way" I think most
people would have understood it as you meant for the original. I'm
just fussy about words.

In many cases, or maybe even most cases, when there is a down
counting loop the code looks cleaner (to be clear, to me) to start
after the end than at the end. For example, the inner loop of an
insertion sort:

T e = elements[i];
for( j = i; j > 0 && before( e, elements[j-1] ); j-- ){
elements[j] = elements[j-1];
}
elements[j] = e;

The invariant is that j is the index of a "hole" that moves down
the array.

Again, my reaction doesn't mean that your way is wrong, or even bad.
People get used to what they get used to. Maybe this relates to our
attitudes about index signedness: I strongly prefer unsigned types
for indexing, and you prefer, I think, signed types. Sometimes
people prefer one choice over another just because it's what they
are used to. For me personally the preference for unsigned index
types is not one of those. But there can be value in having strong
habits even if the habits came about accidentally rather than as a
result of a considered conscious choice.
Tim Rentsch
2024-08-18 21:57:23 UTC
Permalink
Post by Stefan Ram
Post by Mark Summerfield
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
In *cough*C++*cough* you could whip up a "SafeSize" class with
a bulletproof "operator--", so you don't space on the check.
You could still keep cranking out your code in what's basically C and
just cherry-pick this one gnarly feature from that other language.
SafeSize& operator--()
{ if( value == 0 )
{ throw std::underflow_error("SafeSize decrement underflow"); }
--value;
return *this; }
Besides this facility not being a solution to the underlying
problem, C++ has diverged from C to the point where it is
no longer possible to program in C++ as "basically C".
Andrey Tarasevich
2024-08-20 13:53:54 UTC
Permalink
Post by Mark Summerfield
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.)
Post by Mark Summerfield
So I need to guard against this. Here is an example I'm using
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.
Post by Mark Summerfield
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
Andrey Tarasevich
2024-08-20 13:55:40 UTC
Permalink
Post by Andrey Tarasevich
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)
    ...
Sorry, a typo. Was meant to be

for (some_unsigned_type i = size; i != (some_unsigned_type) -1; --i)
...
--
Best regards,
Andrey
Andrey Tarasevich
2024-08-20 13:56:59 UTC
Permalink
Post by Andrey Tarasevich
Post by Andrey Tarasevich
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)
     ...
Sorry, a typo. Was meant to be
  for (some_unsigned_type i = size; i != (some_unsigned_type) -1; --i)
    ...
Of, crap. One more time

for (some_unsigned_type i = size - 1;
i != (some_unsigned_type) -1;
--i)
...

Should be good now :)
--
Best regards,
Andrey
Tim Rentsch
2024-08-22 08:38:24 UTC
Permalink
Post by Andrey Tarasevich
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)
...
Sorry, a typo. Was meant to be
for (some_unsigned_type i = size; i != (some_unsigned_type) -1; --i)
...
Of, crap. One more time
for (some_unsigned_type i = size - 1;
i != (some_unsigned_type) -1;
--i)
...
Should be good now :)
To me the redundant cast is a red flag. A cleaner alternative:

for( some_unsigned_type i = size; i > 0 && i--; ){
...
}

Produces identical code to the above at -O1 (both gcc and clang).
Tim Rentsch
2024-08-22 08:31:01 UTC
Permalink
Post by Mark Summerfield
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.)
Post by Mark Summerfield
So I need to guard against this. Here is an example I'm using
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.
Better (please ignore cosmetic layout differences):

for( size_t i = v->_size; i > index; i-- ){
v->_values[i] = v->_values[i-1];
}
Ike Naar
2024-08-22 11:19:24 UTC
Permalink
Post by Tim Rentsch
Post by Andrey Tarasevich
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.
for( size_t i = v->_size; i > index; i-- ){
v->_values[i] = v->_values[i-1];
}
Or even get rid of the for loop, and use memmove() :

memmove(v->_values + index + 1, v->_values + index,
(v->_size - index) * sizeof *v->_values);
Tim Rentsch
2024-08-22 13:12:53 UTC
Permalink
Post by Ike Naar
Post by Tim Rentsch
Post by Andrey Tarasevich
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.
for( size_t i = v->_size; i > index; i-- ){
v->_values[i] = v->_values[i-1];
}
memmove(v->_values + index + 1, v->_values + index,
(v->_size - index) * sizeof *v->_values);
Yes, although that ignores the context of the question
that was asked, about how to deal with loop index
variables in the presence of possible "underflow".
Bonita Montero
2024-08-24 17:49:09 UTC
Permalink
Post by Mark Summerfield
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++;
}
Ultra-ugly, better use C++ and std::vector<>::emplace_back()/emplace().
Sjouke Burry
2024-08-25 04:30:39 UTC
Permalink
Post by Bonita Montero
Post by Mark Summerfield
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++;
}
Ultra-ugly, better use C++ and std::vector<>::emplace_back()/emplace().
Ultra stupid. This is a c newsgroup.
Bonita Montero
2024-08-25 04:53:49 UTC
Permalink
Post by Sjouke Burry
Post by Bonita Montero
Post by Mark Summerfield
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++;
}
Ultra-ugly, better use C++ and std::vector<>::emplace_back()/emplace().
Ultra stupid. This is a c newsgroup.
Not stupid because in C++ you've got a tenth of the work.
Vir Campestris
2024-08-26 20:39:07 UTC
Permalink
Post by Bonita Montero
Post by Sjouke Burry
Ultra stupid. This is a c newsgroup.
Not stupid because in C++ you've got a tenth of the work.
I come here to read about C. I go to comp.lang.c++ to talk about c++.

I've got a machine over in the corner where c++ is _not_ the right
answer - because it only has a C compiler. There are other reasons to
stick to C too, and this is the place to talk about them.

Andy
Bonita Montero
2024-08-27 16:11:42 UTC
Permalink
Post by Mark Summerfield
Many C std. lib. functions accept and/or return size_t values esp. for
arrays incl. char* strings.
In view of this I'm using size_t throughout my code for array sizes and
indexes.
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.
So I need to guard against this. Here is an example I'm using
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++;
}
I've also noticed that quite a few array-related algorithms _assume_ that
indexes are signed, so again I have to put in guards to avoid subtracting
below zero when I use size_t when implementing them.
So is it considered best practice to use int, long, long long, or size_t,
in situations like these?
I adhere to the semantics some of the C++-containers which use a size_t
as an index. So I use a size_t for indices. And if I need to iterate
down to and including zero I use a ptrdiff_t. On all systems with a
flat address space size_t and ptrdiff_t are as wide as a pointer.
And if I need the largest integer that fits into a general purpose
register I'm also using a size_t, thereby beging more efficient than
using a uin64_6 on a 32 bit platform.

Loading...