Question

I have a big number stored in a string and try to extract a single digit. But what are the differences between those calls?

#include <iostream>
#include <string>

int main(){
    std::string bigNumber = "93485720394857230";
    char tmp = bigNumber.at(5);
    int digit = atoi(&tmp);
    int digit2 = atoi(&bigNumber.at(5))
    int digit3 = atoi(&bigNumber.at(12));
    std::cout << "digit: " << digit << std::endl;
    std::cout << "digit2: " << digit2 << std::endl;
    std::cout << "digit3: " << digit3 << std::endl;
}

This will produce the following output.

digit: 7

digit2: 2147483647

digit3: 57230

The first one is the desired result. The second one seems to me to be a random number, which I cannot find in the string. The third one is the end of the string, but not just a single digit as I expected, but up from the 12th index to the end of the string. Can somebody explain the different outputs to me?

EDIT: Would this be an acceptable solution?

char tmp[2] = {bigNumber.at(5), '\0'};
int digit = atoi(tmp);
std::cout << "digit: " << digit << std::endl;
Was it helpful?

Solution

It is all more or less explicable.

int main(){
    std::string bigNumber = "93485720394857230";

This line copies the single character '5' into the character variable. atoi will convert this correctly. atoi expects that the string parameter is a valid 0 terminated string. &tmp is only a pointer to the character variable - the behaviour of this call is undefined since the memory immediately following the character in memory is unknown. To be exact, you would have to create a null terminated string and pass that in.*

    char tmp = bigNumber.at(5);
    int digit = atoi(&tmp);

This line gets a pointer to the character in position 5 in the string. This happens to be a pointer into the original big number string above - so the string parameter to atoi looks like the string "5720394857230". atoi will clearly oveflow trying to turn this into an integer since no 32 bit integer will hold this.

    int digit2 = atoi(&bigNumber.at(5))

This line gets a pointer into the string at position 12. The parameter to atoi is the string "57230". This is converted into the integer 57230 correctly.

    int digit3 = atoi(&bigNumber.at(12));

... }

Since you are using C++, there are nicer methods to convert strings of characters into integers. One that I am partial to is the Boost lexical_cast library. You would use it like this:

char tmp = bigNumber.at(5);
// convert the character to a string then to an integer
int digit = boost::lexical_cast<int>(std::string(tmp));

// this copies the whole target string at position 5 and then attempts conversion
// if the conversion fails, then a bad_lexical_cast is thrown
int digit2=boost::lexical_cast<int>(std::string(bigNumber.at(5)));

* Strictly, atoi will scan through the numeric characters until a non-numeric one is found. It is clearly undefined when it would find one and what it will do when reading over invalid memory locations.

OTHER TIPS

I know why the 2nd number is displayed.

From the atoi reference.

If the correct value is out of the range of representable values, INT_MAX or INT_MIN is returned.

2147483647 is INT_MAX

bigNumber.at() doesn't return a new string with a single character but the address of a character in the string. So the second call is actually:

atoi("720394857230")

which causes the internal algorithm to overflow.

Also, the first call is very dangerous since it depends on the (random) value in memory at (&tmp)+1.

You have to allocate a string with two characters, assign the single character from bigNumber.at() to the first and \0 to the second and then call atoi() with the address of the temporary string.

The argument to atoi should be a zero-terminated string.

Function at gives pointer to char in the string. Function atoi converts string to int, not only one char.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top