C++ crashes in a 'for' loop with a negative expression

前端 未结 8 911
遇见更好的自我
遇见更好的自我 2020-12-24 11:00

The following code crashes C++ with a runtime error:

#include 

using namespace std;

int main() {
    string s = \"aa\";
    for (int i = 0; i         


        
相关标签:
8条回答
  • 2020-12-24 11:00

    Since s.length() is unsigned type quantity, when you do s.length()-3, it becomes negative and negative values are stored as large positive values (due to unsigned conversion specifications) and the loop goes infinite and hence it crashes.

    To make it work, you must typecast the s.length() as :

    static_cast < int > (s.length())

    0 讨论(0)
  • 2020-12-24 11:00

    The problem you are having arises from the following statement:

    i < s.length() - 3
    

    The result of s.length() is of the unsigned size_t type. If you imagine the binary representation of two:

    0...010

    And you then substitute three from this, you are effectively taking off 1 three times, that is:

    0...001

    0...000

    But then you have a problem, removing the third digit it underflows, as it attempts to get another digit from the left:

    1...111

    This is what happens no matter if you have an unsigned or signed type, however the difference is the signed type uses the Most Significant Bit (or MSB) to represent if the number is negative or not. When the undeflow occurs it simply represents a negative for the signed type.

    On the other hand, size_t is unsigned. When it underflows it will now represent the highest number size_t can possibly represent. Thus the loop is practically infinite (Depending on your computer, as this effects the maximum of size_t).

    In order to fix this problem, you can manipulate the code you have in a few different ways:

    int main() {
        string s = "aa";
        for (size_t i = 3; i < s.length(); i++) {
    
        }
    }
    

    or

    int main() {
        string s = "aa";
        for (size_t i = 0; i + 3 < s.length(); i++) {
    
        }
    }
    

    or even:

    int main() {
        string s = "aa";
        for(size_t i = s.length(); i > 3; --i) {
    
        }
    }
    

    The important things to note is that the substitution has been omitted and instead addition has been used elsewhere with the same logical evaluations. Both the first and last ones change the value of i that is available inside the for loop whereas the second will keep it the same.

    I was tempted to provide this as an example of code:

    int main() {
        string s = "aa";
        for(size_t i = s.length(); --i > 2;) {
    
        }
    }
    

    After some thought I realised this was a bad idea. Readers' exercise is to work out why!

    0 讨论(0)
  • 2020-12-24 11:03

    The type of s.length() is size_t with a value of 2, therefore s.length() - 3 is also an unsigned type size_t and it has a value of SIZE_MAX which is implementation defined (which is 18446744073709551615 if its size is 64 bit). It is at least 32 bit type (can be 64 bit in 64 bit platforms) and this high number means an indefinite loop. In order to prevent this problem you can simply cast s.length() to int:

    for (int i = 0; i < (int)s.length() - 3; i++)
    {
              //..some code causing crash
    }
    

    In the second case len is -1 because it is a signed integer and it does not enter the loop.

    When it comes to crashing, this "infinite" loop is not the direct cause of the crash. If you share the code within the loop you can get further explanation.

    0 讨论(0)
  • 2020-12-24 11:09

    The reason is the same as int a = 1000000000; long long b = a * 100000000; would give error. When compilers multiplies these numbers it evaluates it as ints, since a and literal 1000000000 are ints, and since 10^18 is much more large than the upper bound of int, it will give error. In your case we have s.length() - 3, as s.length() is unsigned int, it cant be negative, and since s.length() - 3 is evaluated as unsigned int, and its value is -1, it gives error here too.

    0 讨论(0)
  • 2020-12-24 11:12

    Actually, in the first version you loop for a very long time, as you compare i to an unsigned integer containing a very large number. The size of a string is (in effect) the same as size_t which is an unsigned integer. When you subtract the 3 from that value it underflows and goes on to be a big value.

    In the second version of the code, you assign this unsigned value to a signed variable, and so you get the correct value.

    And it's not actually the condition or the value that causes the crash, it's most likely that you index the string out of bounds, a case of undefined behavior.

    0 讨论(0)
  • 2020-12-24 11:19

    s.length() is unsigned integer type. When you subtract 3, you make it negative. For an unsigned, it means very big.

    A workaround (valid as long the string is long up to INT_MAX) would be to do like this:

    #include <string>
    
    using namespace std;
    
    int main() {
    
        string s = "aa";
    
        for (int i = 0; i < static_cast<int> (s.length() ) - 3; i++) {
    
        }
    }
    

    Which would never enter the loop.

    A very important detail is that you have probably received a warning "comparing signed and unsigned value". The problem is that if you ignore those warnings, you enter the very dangerous field of implicit "integer conversion"(*), which has a defined behaviour, but it is difficult to follow: the best is to never ignore those compiler warnings.


    (*) You might also be interested to know about "integer promotion".

    0 讨论(0)
提交回复
热议问题