3

I'm using a C++ app on my s6 named CppDroid to make a quick program.

How do you use a size_t as limiter for a "for loop" on it's counter?

int c;

//... more codes here...

    for (c=0; c < a.used; ++c)

        //... more codes here...

The a.used is a number of used array that came from a solution to make a dynamic sized array

The error is: comparison of integer of different signs: 'int' and 'size_t' (aka unsigned int)

The for loop is one of an internal nested loops of the program so I want to maintain variable c as an "int" as much as possible.

I've seen examples about Comparing int with size_t but I'm not sure how it can help since it is for an "if" condition.

Community
  • 1
  • 1
Ace Caserya
  • 2,825
  • 3
  • 25
  • 35

3 Answers3

10

Just use std::size_t c instead of int c.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • 2
    That can work technically, to shut the compiler up, but it introduces unsigned arithmetic and conversions. Which tends to introduce bugs. So it's not a good idea. – Cheers and hth. - Alf Feb 03 '16 at 22:24
  • My requirement is not to change type since it may cause more problems on other parts of code. I did try this and it did gave me more warnings. – Ace Caserya Feb 03 '16 at 22:36
  • 3
    @Cheersandhth.-Alf: Except `a.used` is already a `size_t`, and you have to assume it can take most if not all possible values. You've got `unsigned` arithmetic no matter what. If you want to be paranoid, verify that `a.used` != SIZE_MAX`, but using `size_t` for the loop counter is the only way to make this safe and correct, otherwise you can end up either terminating the loop early (if `(int)a.used` causes wrapping) or never terminating (if `a.used > INT_MAX`, maybe `UINT_MAX`, then `c` wraps before it ever hits the loop termination condition). – ShadowRanger Feb 03 '16 at 22:40
  • @ShadowRanger: No, exceeding the range for an array size, is not a concern in modern programming. It was a concern in 16-bit programming. But even with 32-bit programming the signed index size exceeds any array size you can have. So, it's basically a straw-man argument. I'm sure you've just picked it up somewhere, because one can't land there by reasoning. – Cheers and hth. - Alf Feb 03 '16 at 22:44
  • 4
    @Cheersandhth.-Alf On a typical modern GNU/Linux system (x86_64), `int` has 32 bits, `size_t` has 64 bits, and allocations of more than 2GB or even 4GB are possible. Do you think that's an atypical system? I don't. It won't be the most used environment, but it's still common enough to worry about. –  Feb 03 '16 at 23:03
  • @hvd: Those are facts and very irrelevant. Why do you mention them? It's dumber than dumb, but I see your comment got 2 upvotes, so it probably struck an associative note with some readers. That's SO for you. A voting arena for free-associaters. – Cheers and hth. - Alf Feb 03 '16 at 23:29
  • @hvd: It could help readers if you could make **explicit** your reasoning. I think you're maybe thinking that `int` isn't sufficient. As a kind of misguided and just plain illogical criticism of using `ptrdiff_t`. Can you confirm that that's your error? – Cheers and hth. - Alf Feb 03 '16 at 23:36
  • 2
    @Cheersandhth.-Alf You defended using `int` for loop variables when they represent array indices, saying that isn't a problem in modern programming. I gave an example of a modern system where it is. So yes, an example where `int` isn't sufficient. Seems relevant enough to me. I do not see why you are bringing `ptrdiff_t` into this though. –  Feb 03 '16 at 23:48
  • @hvd: No, I didn't defend using `int` for loop variables in general. That's a straw man argument. I recommended using `ptrdiff_t` for the general case, such as an `n_items` function shown in my answer, and here, in the comments, I recommended against using `size_t`. That should not be possible to misunderstand. But such things happen. – Cheers and hth. - Alf Feb 03 '16 at 23:51
  • Could you guys like take it offline or in some chatroom please? I keep receiving notifications for this. [Here](http://chat.stackoverflow.com/rooms/info/102534/alf-vs-hvd?tab=general), I made one just for you. – Shoe Feb 03 '16 at 23:57
  • @hvd: No, it doesn't break, unless the OP's original code was faulty. So this is your second incorrect assertion. Just to be clear, your error this time is to conflate the general case with a specific case, reasoning that any car has to be designed for race track and pickup duty, plus some, to cover all generalities, which is not a good approach in any engineering discipline. – Cheers and hth. - Alf Feb 03 '16 at 23:59
  • 3
    @Cheersandhth.-Alf Your answer uses `int` for `c` and `n` without checking that the values you want to store in them fit. I will be happy to construct a simple program tomorrow to show precisely how this will fail on my system. (Excuse my earlier reply to your last comment, I didn't read carefully enough. Rewritten now, the point stands.) "unless the OP's original code was faulty.": it was. –  Feb 03 '16 at 23:59
  • 1
    @hvd: You can't break it unless you have more items in your array than the OP has. I.e. unless you introduce an error that wasn't there. Assuming his/her code is correct. It is ... just the OP's code. – Cheers and hth. - Alf Feb 04 '16 at 00:00
  • Seriously guys. I don't care, please go in the chatroom and talk there and maybe link it once here. – Shoe Feb 04 '16 at 00:02
7

As long as a.used doesn't change during the iteration, a common idiom is:

for(int c=0, n=a.used; c<n; ++c) {
    ...
}

In this way, the cast happens implicitly, and you also have the "total number of elements" variable n handy in the loop body. Also, when n comes from a methods call (say, vec.size()) you evaluate it just once, which is slightly more efficient.1


1. In theory the compiler may do this optimization by itself, but with "complicated" stuff like std::vector and a nontrivial loop body it's surprisingly difficult to prove that it's a loop invariant, so often it's just recalculated at each iteration.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
2

Regarding

comparison of integer of different signs: 'int' and 'size_t' (aka unsigned int)

… this is a warning. It's not an error that prevents creation of an executable, unless you've asked the compiler to treat warnings as errors.

A very direct way to address it is to use a cast, int(a.size).

More generally I recommend defining a common function to do that, e.g. named n_items (C++17 will have a size function, unfortunately with unsigned result and conflating two or more logical functions, so that name's taken):

using My_array = ...; // Whatever

using Size = ptrdiff_t;

auto n_items( My_array const& a )
    -> Size
{ return a.used; }

then for your loop:

for( int c = 0; c < n_items( a ); ++c )

By the way it's generally Not A Good Idea™ to reuse a variable, like c here. I'm assuming that that reuse was unintentional. The example above shows how to declare the loop variable in the for loop head.


Also, as Matteo Italia notes in his answer, it can sometimes be a good idea to manually optimize a loop like this, if measuring shows it to be a bottleneck. That's because the compiler can't easily prove that the result of the n_items call, or any other dynamic array size expression, is the same (is “invariant”) in all executions of the loop body.

Thus, if measuring tells you that the possibly repeated size expression evaluations are a bottleneck, you can do e.g.

for( int c = 0, n = n_items( a ); c < n; ++c )

It's worth noting that any manual optimization carries costs, which are not easy to measure, but which are severe enough that the usual advice to is to defer optimization until measurements tell you that it's really needed.

Community
  • 1
  • 1
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • To be clear, as we talked about yesterday, using `int` will cause problems for arrays with more than `INT_MAX` elements. Am I understanding you correctly that you don't consider that a flaw in the code, but rather a flaw in the code that passes in such an array? Either way, it deserves a warning, in my opinion, considering how badly it breaks. A simple example is [this one](http://goo.gl/xAsMYE), which segfaults because `c` overflows to `INT_MIN`, leading to an out-of-bounds access. –  Feb 05 '16 at 18:17
  • @hvd: You're right that if the array has more than `INT_MAX` items, then the OP's code is faulty. However, the "passes in" doesn't make sense to me, sorry. The loop (specific case) is not a function, so there the "passes in" doesn't fit, and the function (general case) doesn't use `int` but `ptrdiff_t`, so there there is no size limitation. – Cheers and hth. - Alf Feb 06 '16 at 00:56
  • Yeah, that was sloppily worded, sorry. The `for` loop would appear inside a function. I meant if that function got passed a large array. The dynamic array implementation that the OP is using uses `size_t` for the length, so it would be possible to pass in such a large array. –  Feb 06 '16 at 08:40
  • @hvd: Well, unbeknownst to you, the array has exactly 16 items, the hex digits. :) Or something else of that sort. We don't know. It could be that the `// More codes here` comment hides a grave error, which would would never have occurred had there been braces around the loop body. Hm. – Cheers and hth. - Alf Feb 06 '16 at 08:46
  • Yup. And the fact that we don't know tells me that we shouldn't assume the length will fit in an `int`. To you, it indicates that we shouldn't assume we need to worry about special cases, right? –  Feb 06 '16 at 08:56
  • @hvd: I don't "improve" presented code in the ways I can image that it could be faulty. The code in my answer would then not resemble the OP's code very much. That would not be good for the OP. I strongly **suggest that you stop doing it**. It's hopelessly counter-productive and is not helpful in any way. – Cheers and hth. - Alf Feb 06 '16 at 09:06