Hello there,

is there a way to convert a char[2] = '1', '2' kinda thing to an int ?

>is there a way to convert a char[2] = '1', '2' kinda thing to an int ?
Yes. It's easiest to store the characters as a string first though, so that you can take advantage of standard solutions without resorting to something manual. A good solution is using strtol to make the conversion:

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

int main ( void )
{
  char buffer[BUFSIZ];

  while ( fgets ( buffer, sizeof buffer, stdin ) != NULL ) {
    char *end;
    int value;

    value = (int)strtol ( buffer, &end, 0 );

    if ( end != buffer )
      printf ( "Value = %d\n", value );
    else
      puts ( "No conversion made!" );
  }

  return 0;
}

Just for grins, you can also do it manually. Though as you can see, getting it right is a smidge more complicated than using the standard library:

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

const char *parse_int ( const char *s, int *value );

int main ( void )
{
  char buffer[BUFSIZ];

  while ( fgets ( buffer, sizeof buffer, stdin ) != NULL ) {
    int x;
    const char *p;

    buffer[strcspn ( buffer, "\n" )] = '\0';
    
    errno = 0;
    p = parse_int ( buffer, &x );

    if ( errno != 0 )
      puts ( "Overflow!" );

    if ( *p == '\0' )
      puts ( "Successful conversion!" );
    else
      printf ( "Unconverted: \"%s\"\n", p );

    if ( p != buffer )
      printf ( "Value = %d\n", x );
    else
      puts ( "No value found" );
  }

  return 0;
}

#include <ctype.h>
#include <errno.h>
#include <limits.h>

const char *parse_int ( const char *s, int *value )
{
  /* Base of the final converted value */
  const unsigned base = 10;

  /* Largest possible value without the least significant digit */
  const unsigned limit = UINT_MAX / base;

  /* Least significant digit from the largest possible value */
  const unsigned top_digit = UINT_MAX % base;

  unsigned overflow = 0; /* True if integer overflow occurs */
  unsigned sign = 0;     /* Final sign of the converted value */

  unsigned temp = 0;     /* The intermediate converted value */
  unsigned n = 0;        /* Count of converted digits */

  /* Save and skip over the sign if present */
  if ( *s == '-' || *s == '+' )
    sign = *s++ == '-';

  /* Build the intermediate value */
  for ( ; isdigit ( *s ); s++, n++ ) {
    unsigned digit = *s - '0';
    
    /*
      This protects *only* the intermediate value
      from overflow. Overflow of the final value
      requires further checks
    */
    overflow =  temp > limit
      || ( temp == limit && digit > top_digit );

    if ( overflow )
      break;

    /* Shift-add by the base */
    temp = temp * base + digit;
  }

  if ( n > 0 ) {
    /*
      A conversion was made, so now we need to
      deal with overflow and set the final value
    */
    if ( overflow
      || ( sign && temp > -INT_MIN )
      || ( !sign && temp > INT_MAX ) )
    {
      /*
        The intermediate actually overflowed,
        or converting it to int would overflow.
        Either way it's an error to the caller
      */
      errno = ERANGE;
      temp = sign ? -INT_MIN : INT_MAX;
    }

    *value = sign ? -(int)temp : (int)temp;
  }
  else if ( sign ) {
    /*
      We found a sign and skipped over it. But
      because no conversion was made, we need
      to "unskip" the sign
    */
    --s;
  }

  return s;
}

Whatever you do, don't use atoi. It's an evil function that doesn't handle errors well. The only safe way to use atoi is to basically parse the string first to make sure that it's a valid integer. If you're going to do that, you might as well convert it manually in the process like parse_int. :icon_rolleyes:

yeah ,actually there is a lot simple way to do it, but not so robust like Narue's solution:
Just subtracting 48 from that character ('1' or '2' or whatever you got) will give you the integer version of it. (because of the ASCII character codes)

>actually there is a lot simple way to do it
That's exactly what my parse_int does. The only difference is that I added a lot of necessary checks and extra logic for a generalized solution. A naive approach would look like this:

#include <stdio.h>

int parse_int ( const char *s );

int main ( void )
{
  char buffer[BUFSIZ];

  while ( fgets ( buffer, sizeof buffer, stdin ) != NULL )
    printf ( "Value = %d\n", parse_int ( buffer ) );

  return 0;
}

#include <ctype.h>

int parse_int ( const char *s )
{
  unsigned sign = 0; /* Sign of the converted value */
  unsigned result = 0; /* The converted value (no sign) */

  /* Save and skip over the sign if present */
  if ( *s == '-' || *s == '+' )
    sign = *s++ == '-';

  /* Build the result value */
  while ( isdigit ( *s ) )
    result = result * 10 + ( *s++ - '0' );

  // Add the sign and force integer range
  return sign ? -(int)result : (int)result;
}

That's about as simple as it gets without being blatantly wrong or assuming a certain number of characters in the result. You could simplify it significantly if you took the OP's example and assumed only two digit values:

#include <stdio.h>

int main ( void )
{
  char buffer[BUFSIZ];

  while ( fgets ( buffer, sizeof buffer, stdin ) != NULL ) {
    printf ( "Value = %d\n",
      ( 10 * ( buffer[0] - '0' ) ) + buffer[1] - '0');
  }

  return 0;
}

>because of the ASCII character codes
No. C doesn't require characters to have ASCII values. What C does require is that the values of the decimal digits have to be consecutive. That's why subtracting '0' works for the characters '0' through '9', regardless of the character set being used.

This article has been dead for over six months. Start a new discussion instead.