tags:

views:

708

answers:

6

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;
+3  A: 

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.

Aaron Digulla
Lucas
atoi() will stop on any non-numeric character, so you have a chance of 96% (245 to 10) that it works.
Aaron Digulla
+4  A: 

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

Ólafur Waage
+3  A: 

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

Alan
+5  A: 

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.

1800 INFORMATION
Thank you for this fancy solution. Is using lexical_cast safer than using atoi() or why do you suggest to use it?
Lucas
lexical_cast is a lot safer because it takes advantage of modern C++ idioms to give type safety. You specify the target and source types (the source is inferred in the code above) - it will check for errors along the way and throw exceptions in the case if it was unable to perform the conversion. atoi is an older style API from the C standard library. lexical_cast has additional advantages in that it can work with many more defined types (including your own defined types and classes)
1800 INFORMATION
Thank you for the clarification. I think you need to at the size of the string in your string constructor, so "string ( size_t n, char c );" can be called. I now use:int digit=boost::lexical_cast<int>(std::string(1, bigNumber.at(5)));
Lucas
Or digit=boost::lexical_cast<int>(std::string( to call string ( const char * s, size_t n ).
Lucas
Please be aware that boost::lexical_cast is much slower as atoi. I also use it very often in a performance non-critical code. The problem with lexical_cast is that it uses stringstream for conversion. If you are working in a multi-threaded environement any stream class from the standard lib will use locks on a mutex for every character being inserted, even if the stream object is used from a single thread.Your number consisting of 17 chars will involve 17 mutex locks when put into stream.
ovanes
That's good to know. However, I think that you should generally favour obviously correct code over more risky (but performant) alternatives unless you are sure you are running in the critical path for performance - therefore I would recommend using streams, type safety, lexical cast and that kind of thing by default
1800 INFORMATION
+3  A: 

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

Kirill V. Lyadvinsky
A: 

I'm using atoi to conver a single character into an integer, which works fine the first time. When I do this a second time though, it keeps the first value for some reason.

zari