• size_t best practice

    From Mark Summerfield@21:1/5 to All on Sun Aug 18 08:03:08 2024
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ike Naar@21:1/5 to Mark Summerfield on Sun Aug 18 08:38:13 2024
    On 2024-08-18, Mark Summerfield <mark@qtrac.eu> wrote:
    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):

    [...]

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Mark Summerfield on Sun Aug 18 12:36:49 2024
    On Sun, 18 Aug 2024 08:03:08 +0000
    Mark Summerfield <mark@qtrac.eu> wrote:

    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?

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Mark Summerfield@21:1/5 to Ike Naar on Sun Aug 18 10:15:52 2024
    On Sun, 18 Aug 2024 08:38:13 -0000 (UTC), Ike Naar wrote:
    [snip]
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Mark Summerfield on Sun Aug 18 04:32:40 2024
    Mark Summerfield <mark@qtrac.eu> writes:

    [what type to use for array indexing?]

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Mark Summerfield on Sun Aug 18 12:17:36 2024
    Mark Summerfield <mark@qtrac.eu> wrote or quoted:
    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; }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Stefan Ram on Sun Aug 18 15:40:13 2024
    On 18 Aug 2024 12:17:36 GMT
    ram@zedat.fu-berlin.de (Stefan Ram) wrote:

    Mark Summerfield <mark@qtrac.eu> wrote or quoted:
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Stefan Ram on Sun Aug 18 14:57:23 2024
    ram@zedat.fu-berlin.de (Stefan Ram) writes:

    Mark Summerfield <mark@qtrac.eu> wrote or quoted:

    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".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Sun Aug 18 15:23:58 2024
    Michael S <already5chosen@yahoo.com> writes:

    On 18 Aug 2024 12:17:36 GMT
    ram@zedat.fu-berlin.de (Stefan Ram) wrote:

    Mark Summerfield <mark@qtrac.eu> wrote or quoted:

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Tim Rentsch on Mon Aug 19 11:13:03 2024
    On Sun, 18 Aug 2024 15:23:58 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On 18 Aug 2024 12:17:36 GMT
    ram@zedat.fu-berlin.de (Stefan Ram) wrote:

    Mark Summerfield <mark@qtrac.eu> wrote or quoted:

    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.

    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)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Mon Aug 19 09:43:38 2024
    Michael S <already5chosen@yahoo.com> writes:

    On Sun, 18 Aug 2024 15:23:58 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On 18 Aug 2024 12:17:36 GMT
    ram@zedat.fu-berlin.de (Stefan Ram) wrote:

    Mark Summerfield <mark@qtrac.eu> wrote or quoted:

    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.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Mark Summerfield on Tue Aug 20 06:53:54 2024
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Andrey Tarasevich on Tue Aug 20 06:56:59 2024
    On 08/20/24 6:55 AM, Andrey Tarasevich wrote:
    On 08/20/24 6:53 AM, Andrey Tarasevich wrote:

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Andrey Tarasevich on Tue Aug 20 06:55:40 2024
    On 08/20/24 6:53 AM, Andrey Tarasevich wrote:

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Mark Summerfield on Tue Aug 20 07:38:35 2024
    On 08/18/24 3:15 AM, Mark Summerfield wrote:

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Andrey Tarasevich on Thu Aug 22 01:31:01 2024
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

    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.

    Better (please ignore cosmetic layout differences):

    for( size_t i = v->_size; i > index; i-- ){
    v->_values[i] = v->_values[i-1];
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Andrey Tarasevich on Thu Aug 22 01:38:24 2024
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

    On 08/20/24 6:55 AM, Andrey Tarasevich wrote:

    On 08/20/24 6:53 AM, Andrey Tarasevich wrote:

    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).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ike Naar@21:1/5 to Tim Rentsch on Thu Aug 22 11:19:24 2024
    On 2024-08-22, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

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

    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);

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Ike Naar on Thu Aug 22 06:12:53 2024
    Ike Naar <ike@sdf.org> writes:

    On 2024-08-22, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

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

    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);

    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".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Sjouke Burry@21:1/5 to Bonita Montero on Sun Aug 25 06:30:39 2024
    On 24.08.24 19:49, Bonita Montero wrote:
    Am 18.08.2024 um 10:03 schrieb 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Vir Campestris@21:1/5 to Bonita Montero on Mon Aug 26 21:39:07 2024
    On 25/08/2024 05:53, Bonita Montero wrote:
    Am 25.08.2024 um 06:30 schrieb 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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)