4

The program will print "Entered the loop" if I use input.size() - 1 as the for loop condition.

std::string input;
input = {""};
int i = 0;
for (; i < input.size() - 1; ++i)
{
    cout << "Entered the loop" << endl;
}

However, if I pass the value of input.size() -1 to an integer (checksize):

std::string input;
input = {""};
int checksize = input.size() - 1;
int i = 0;
for (; i < checksize; ++i)
{
    cout << "Entered the loop" << endl;
}

then the program will not go into the loop and will not print "Entered the loop"

I was wondering why this happen? It seems the two pieces of code are the same to me.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
lightrek
  • 951
  • 3
  • 14
  • 30

2 Answers2

7

You are a victim of unsigned integers :)

std::string::size() returns an unsigned integer (of type equivalent to size_t).

When the compiler evaluates input.size() - 1, this kind of becomes size_t(0) - 1, and since the computation is done with unsigned integers, instead of -1 you get a very big integer number (MSVC 32-bit compiler prints 4294967295, which corresponds to the maximum 32-bit unsigned integer value 2^32 - 1).

So this loop:

for (int i = 0; i < input.size() - 1; ++i)

is kind of equivalent to:

for (int i = 0; i < /* very big positive number */; ++i)

which will print your message many times.

Instead, in the second case, when you evaluate input.size() - 1 and then assign it to an int variable (which is signed by default), the compiler still computes size_t(0) - 1 as a very big positive integer number, but then this number is converted to a (signed) int, resulting in checksize being initialized with -1, and your loop is never executed:

for (int i = 0; i < checksize /* -1 */; ++i)

Considering this compilable code:

#include <iostream>
#include <string>
using namespace std;

int main() 
{
    string input;

#ifdef CASE1
    for (int i = 0; i < input.size() - 1; ++i)
    {
        cout << "Entered the loop\n";
    }
#else
    cout << "input.size() - 1  == " << (input.size() - 1) << '\n';
    cout << "SIZE_MAX          == " << SIZE_MAX << '\n';

    int checkSize = input.size() - 1;
    cout << "checkSize == " << checkSize << '\n';

    for (int i = 0; i < checkSize; ++i)
    {
        cout << "Entered the loop\n";
    }
#endif
}

If you compile its CASE1 with MSVC and /W4 (warning level 4, which I highly suggest), you get a warning for your for loop condition:

cl /EHsc /W4 /nologo /DCASE1 test.cpp

test.cpp(10) : warning C4018: '<' : signed/unsigned mismatch

which usually points to you that something is wrong with your code.

Instead, compiling without CASE1, gives no warnings and the following output (which shows that the for loop's body is never executed):

cl /EHsc /W4 /nologo test.cpp

input.size() - 1  == 4294967295
SIZE_MAX          == 4294967295
checkSize == -1
Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 2
    `input.size() - 1` is an unsigned computation, even if later assigned to int. The compiler doesn't compute `0 - 1` as such; it converts 4294967295 to int . (which is not guaranteed to give the same result, but probably will) – M.M May 07 '16 at 15:31
2

input.size() is an unsigned quantity. So when it is zero, subtracting 1 gives the maximum value for its type (a large positive integer, probably SIZE_MAX).

So the loop is entered because 0 < SIZE_MAX is true.

But when you convert this large positive number to int, the number is out of range for int. So implementation-defined behaviour occurs, which probably produces checksize == -1. Then your loop is not entered because 0 < -1 is false.

M.M
  • 138,810
  • 21
  • 208
  • 365