Pictured is a peer learning day occuring

 

Atoi in C


In this, the first of a series of C function tutorials I will be sharing over the course of the next few weeks, I’d like to go over a helpful conversion function - atoi.

PREFACE - WHAT IS C?

More than just the third letter of the alphabet, C is a compiled programming language - in other words, C is run by a computer only after being compiled. Compilation is accomplished by software called compilers, which take C source code files and translate them into executable language (binary) that computers can run.

There are many different C compilers, but in this tutorial, I will be using one called GCC, published by GNU. For instructions on how to install GCC, you can visit GNU’s installation guide here (https://gcc.gnu.org/install/).

Throughout the duration of this article, I exemplify usage of GCC on the command line. If you are unfamiliar with what a command line is, read more here (http://linuxcommand.org/index.php).

To read more on what C is and how to use it, read my earlier post on the topic (Learn C)

ATOI - WHAT

The function atoi is a C standard library function that converts strings to numbers (more specifically, integers).

PARAMETERS
const char *str

The function receives one parameter, a string. Note that the string is received as a constant - the function atoi will never alter a received string; only return its converted integer value.

ASIDE - STRING REFRESHER
When working with strings in C, remember - strings are no more than arrays of ASCII-encoded characters ending with a terminating null byte (\0).

RETURN VALUE
int

If the received string can be successfully converted to a number, the function returns the converted integer. Otherwise, it returns 0.

What exactly is a string that can be successfully converted to a number? Well, it turns out that the behavior here is a bit funky.

In general, strings that can successfully be converted to numbers strictly include the following:

  • Strings consisting exclusively of ASCII digits '0123456789'
  • Strings consisting exclusively of ASCII digits and starting with the character '+'
  • Strings consisting exclusively of ASCII digits and starting the with character '-'

However, this does not mean that you will receive 0 for any other kind of string passed to atoi.

The function atoi works incrementally; in other words, it builds numbers represented by strings one-by-one, iterating over characters from left to right. Only upon encountering a non-ASCII digit (assuming it is not a '+' or '-' at the beginning of the string), does the function break.

After breaking, atoi returns one of two possible values - if the function is at the beginning of the string and has not converted any values, it returns 0. Otherwise, it returns the current number it has built thus far.

In briefer summation, a more proper description of atoi’s return value is the first valid number that can be converted from the received string, or 0.

DECLARATION

The function atoi is declared as follows:

/**
* atoi - Converts a string to an integer.
* @str: The string to convert.
*
* Return: The int value of the first valid number that can be converted from @str.
*         Otherwise, 0.
*/
int atoi(const char *str);

ATOI - HOW

To use the function atoi, include the C standard library using the header <stdlib.h>.

#include <stdlib.h>

Once the C standard library has been included, you can call the function atoi on any string directly. When using the function, remember - the function returns the first valid number that can be converted from the received string. If no number can be converted whatsoever, it returns 0.

Example (note that the library <stdio.h> is additionally included here for the usage of printf):

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    printf("All ASCII digits work great: %d\n", atoi("12"));
    printf("All ASCII digits starting with a + sign work great: %d\n", atoi("+12"));
    printf("All ASCII digits starting with a - sign work great: %d\n", atoi("-12"));
    printf("This will only give the initial valid ‘12’: %d\n", atoi("12+34"));
    printf("Separating numbers by spaces? You’ll only get the 1st: %d\n", atoi("12 34"));
    printf("What is this, implicit atoi division? %d\n", atoi("12/34"));
    printf("Hi yourself, but you’ll only get 0 from this: %d\n", atoi("Hi Brennan!"));

    return (EXIT_SUCCESS);
}
$ gcc main.c -o atoi
$ ./atoi
All ASCII digits work great: 12
All ASCII digits starting with a + sign work great: 12
All ASCII digits starting with a - sign work great: -12
This will only give the initial valid ‘12’: 12
Separating numbers by spaces? You’ll only get the 1st: 12
What is this, implicit atoi division? 12
Hi yourself, but you’ll only get 0 from this: 0

ATOI - WHEN

A common use case for the function atoi is the handling of integer parameters received as command-line arguments. Command-line arguments are received in string format; hence, in cases where you need to use command-line arguments as numbers, you must first get the integer value they represent.

To exemplify, take a look at the following calculator function that returns the addition of two integer parameters received on the command line:

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    /* Get the integer value of the first number using atoi */
    int num1 = atoi(argv[1]);
    /* Get the integer value of the second number using atoi */
    int num2 = atoi(argv[2]);
    /* Sum the two converted numbers */
    int sum = num1 + num2;

    printf("%d + %d = %d\n", num1, num2, sum);

    return (EXIT_SUCCESS);
}
$ gcc main.c -o calculator
$./calculator 7 8
7 + 8 = 15

Watch out - keep in mind that atoi will return 0 both for strings with no valid number conversions, and for the actual string "0" (or a string of '0'’s). To account for this behavior, we must implement some error handling. In cases where atoi returns 0, we will double-check that we did not convert a string starting with the '0' character.

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    /* Get the integer value of the first number using atoi */
    int num1 = atoi(argv[1]);
    /* Get the integer value of the second number using atoi */
    int num2 = atoi(argv[2]);
    int sum;

    /* Check that you received valid numbers before summing */
    if (num1 == 0 && *argv[1] != ‘0’)
    {
        printf("The first argument is not a valid number!\n");
        return (EXIT_FAILURE);
    }
    if (num2 == 0 && *argv[2] != ‘0’)
    {
        printf("The second argument is not a valid number!\n");
        return (EXIT_FAILURE);
    }

    /* Sum the two numbers */
    sum = num1 + num2;

    printf("%d + %d = %d\n", num1, num2, sum);
        
    return (EXIT_SUCCESS);
}
$ gcc main.c -o calculator
$./calculator 0 7
0 + 7 = 7
$ ./calculator 18 000
18 + 0 = 18                        

Much better. Thanks, atoi!

ADVANCED 100 - CALCULATOR ERROR HANDLING 3.0

The astute C programmers among you may have noticed yet another edge case with our above calculator function - strings with initial valid numbers but containing non-ASCII digits:

$ ./calculator 7 8gotchya!
7 + 8 = 15

Ah, you did get me! But of course - atoi will return any initial valid integer that can be converted from a string, even if the string as a whole cannot be represented as a number.

We have a zero-tolerance policy for edge cases in our calculator program, so let’s beef up the error handling yet again. After checking cases where atoi returns 0, we’ll additionally loop through the entirety of each of our received strings to ensure that they strictly contain ASCII digits, or start with '+' or '-'.

$ cat main.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    /* Get the integer value of the first number using atoi */
    int num1 = atoi(argv[1]);
    /* Get the integer value of the second number using atoi */
    int num2 = atoi(argv[2]);
    int sum;

    /* Check that you received valid numbers before summing */
    if (num1 == 0 && *argv[1] != '0')
    {
        printf("The first argument is not a valid number!\n");
        return (EXIT_FAILURE);
    }
    for (i = 0; argv[1][i] != '\0'; i++)
    {
        if ((argv[1][i] == '-' || argv[1][i] == '+') && i == 0)
            continue;
        if (argv[1][i] < '0' || argv[1][i] > '9')
        {
            printf("Tried to trick me, didn't you! Not today.\n");
            return (EXIT_FAILURE);
        }
    }
    if (num2 == 0 && *argv[2] != '0')
    {
        printf("The second argument is not a valid number!\n");
        return (EXIT_FAILURE);
    }
    for (i = 0; argv[2][i] != '\0'; i++)
    {
        if ((argv[2][i] == '-' || argv[2][i] == '+') && i == 0)
            continue;
        if (argv[2][i] < '0' || argv[2][i] > '9')
        {
            printf("Tried to trick me, didn't you! Not today.\n");
            return (EXIT_FAILURE);
        }
    }

    /* Sum the two numbers */
    sum = num1 + num2;

    printf("%d + %d = %d\n", num1, num2, sum);
 	 
    return (EXIT_SUCCESS);
}
$ gcc main.c -o calculator
$./calculator 1immagetyou 7
Tried to trick me, didn't you! Not today.
$ ./calculator 7 1nooooooo
Tried to trick me, didn't you! Not today.

What do we say to edge cases? Not today.

ADVANCED 101 - IMPLEMENTATION

Oh, you’re one of those, huh? My kind of people. I present, my implementation of the function atoi.

/**
 * _atoi - Converts a string to an integer.
 * @s: The string to be converted.
 *
 * Return: The integer value of the converted string.
 */
int _atoi(char *s)
{
    int sign = 1;
    unsigned int num = 0;

    do {
        if (*s == '-')
            sign *= -1;

        else if (*s >= '0' && *s <= '9')
            num = (num * 10) + (*s - '0');

        else if (num > 0)
            break;
    } while (*s++);

    return (num * sign);
}

source: https://github.com/bdbaraban/holbertonschool-low_level_programming/blob/master/0x04-pointers_arrays_strings/100-atoi.c

As you can see, I loop over the received string character by character, starting at the beginning. I use a sign flag to keep track of the sign of the number I’m converting, which I flip positive/negative at the ending return statement accordingly.

As long as I am at a character that is a valid ASCII-encoded digit, I use some arithmetic to build up the converted number. I take advantage of the fact that all digits in the ASCII table start at 0, subtracting the ASCII value '0' from the current character and then adding the resulting integer multiplied by 10 to my running conversion ( 1 = (0 * 10) + 1, 19 = (1 * 10) + 9, etc.).

As soon as I encounter a non-ASCII digit, I break with whatever current number I have converted. This will only ever be the first valid number in the string (up to the point I have encountered an invalid character), or my initialized value, 0!

Of course, this is just one, personal implementation of the function strcmp. There are multiple ways to do so; in fact, I encourage, no, challenge, you to find another way to write this function!

NOTE

Examples in this article were compiled and run on a Linux Ubuntu 18.04 LTS machine with GNU GCC version 7.3.0.
Written by:

Brennan Baraban, Cohort 7 (SF Campus)