简体   繁体   中英

How does atoi() function in C++ work?

所以...我知道C ++标准库中的atoi函数应该将字符串转换为整数...它是如何工作的?...(我正在努力学习东西,我只是想知道)。 ..如果您可以向我展示其中的代码或制作您自己的代码,那将会非常感激...提前感谢。

Something like this:

int atoi( const char *c ) {
    int value = 0;
    int sign = 1;
    if( *c == '+' || *c == '-' ) {
       if( *c == '-' ) sign = -1;
       c++;
    }
    while ( isdigit( *c ) ) {
        value *= 10;
        value += (int) (*c-'0');
        c++;
    }
    return value * sign;
}

You loop through the characters in the string as long as they are digits. For each one, add to the counter you're keeping - the value to add is the integer value of the character. This is done by subtracting the ascii value of '0' from the ascii value of the digit in question.

Note that this code doesn't handle overflow. If you pass in "887452834572834928347578423485273" (which won't fit in an int ), the result is undefined.

Digit by digit:

A char * , strictly speaking, is a pointer to a char . A pointer is just an address to some place in memory. In C/C++ (and Java), strings are made up of characters that can, individually, be treated as integers (usually one byte), thanks to ASCII .

In C (and C++), a pointer to an item of some type is the same as a pointer to an array of elements of that type. Strings in pure C are just arrays of char s, with a '\\0' (NUL) at the end so that you know when you've hit the end of a string without having to pass around its length everywhere (a pointer is only an address, it knows nothing about what it points to).

Ignore the const keywords for now.

The C version of atoi loops through each character in the string. The *str++ does several things (it's important to understand how it works, but it's a horrible way to actually write C). It's equivalent to *(str++) . The str++ returns the value of str (a pointer) and then increments it by one (but it returns the old value!). The * "dereferences" the pointer, basically reading in a char from memory. This char is stored in digit and then compared to NUL . Characters are stored in ASCII, which represents digits contiguously, so we can just check that digit is between 0 and 9. We know now that we're reading in a new digit, so we multiply the previous value by 10 to "shift" the value over and then add in the digit.

Pure C version:

int atoi(const char* str) {
  int num = 0;
  char digit;
  while ((digit = *str++) != '\0') {
    if (digit < '0' || digit > '9') {
      return num;  /* No valid conversion possible */
    }
    num *= 10;
    num += c - '0';
  }
  return num;
}

A C++ string is an object to make dealing with strings easier. You can get a char * from a C++ string with .c_str() .

C++ version (more likely an inlined call to the char* version with "return atoi(str.c_str());"):

int atoi(const std::string& str) {
  int n = 0;
  for (int i = 0; i < str.size(); i += 1) {
    char digit = str.at(i);   /* Could probably use iterator here,
                               * but this is more explicit. */
    if (digit < '0' || digit > '9') {
      return n;  /* No valid conversion possible. */
    }
    n *= 10;
    n += digit - '0';
  }
  return n;
}

Edit: Fixed issue where <> would not display properly.

Edit: C++ string version added

Edit: Fixed so it returns 123 in 123a case.

Edit: Changed stray num to n in C++ version

Turn the question around: how do you do it? When you see "31" written down, how do you understand how many of X you need to count out to equal that?

    1 * the value in the leftmost column
+  10 * the value in the next column to the right
+ 100 * the value in the next column to the right
...

Well, you can code that up.


Actually is is often implemented from the right most character for easy of use with streams. How might you do that?

The logic is simply to process each character into it's integer value (adjusting for position within the string).

Here's how I did it in C#. Same general idea.

Here's an implementation that also checks for error conditions and works for any integer types.

#include <limits>
#include <string>
#include <cctype>
#include <cassert>
#include <type_traits>

template<typename TChar, typename TNumber> bool my_atoi(const std::basic_string<TChar>& str, TNumber& result)
{
    typedef std::make_unsigned<TNumber>::type TNumberUnsigned;

    // check if result type is integer
    assert(std::numeric_limits<TNumber>::is_integer);

    auto currChar = str.cbegin();

    // use corresponding unsigned type to accumulate number to avoid overflows for numbers such as -128
    TNumberUnsigned number = 0;

    bool isNegative = *currChar == '-';
    if (isNegative) {
        // negative numebers can only be parsed into signed types
        if (!std::numeric_limits<TNumber>::is_signed)
            return false;
        ++currChar;
    }

    // empty string or string containing just - sign are not valid integers
    if (currChar == str.cend())
        return false;

    while (currChar != str.cend()) {
        auto digit = *currChar - '0';

        // check that the next digit is valid
        if (digit < 0 || digit > 9)
            return false;

        // check for overflow
        if (number > std::numeric_limits<TNumberUnsigned>::max() / 10)
            return false;
        number *= 10;

        // check for overflow
        if (number > std::numeric_limits<TNumberUnsigned>::max() - digit)
            return false;
        number += digit;

        ++currChar;
    }

    if (isNegative) {
        // correctly check for negative overflow (-128)
        if (number > static_cast<TNumberUnsigned>(std::numeric_limits<TNumber>::max()) + 1)
            return false;

        result = static_cast<TNumber>(-1 * number);
    }
    else {
        if (number > static_cast<TNumberUnsigned>(std::numeric_limits<TNumber>::max()))
            return false;

        result = static_cast<TNumber>(number);
    }

    return true;
}

Basically by subtracting ASCII zero ('0') and checking if it is a digit or not . You need to know the position:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int atoi( const char* nptr )
{
    int result   = 0;
    int position = 1;

    const char* p = nptr;
    while( *p )
    {
        ++p;
    }
    for( --p; p >= nptr; p-- )
    {
        if( *p < 0x30 || *p > 0x39 )
        {
            break;
        }
        else
        {
            result += (position) * (*p - 0x30);
            position *= 10;
        }
    }
    result = ((nptr[0] == '-')? -result : result);
    return result;
}

int main()
{
    char buffer[BUFSIZ] = {0};

    printf( "Enter an integer: " );
    fgets( buffer, BUFSIZ, stdin );
    buffer[strlen(buffer)-1] = 0;

    printf( "You entered %d\n", atoi( buffer ) );

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM