2010-04-28 13 views
11

No entiendo los resultados del siguiente código C.¿Por qué obtengo este resultado inesperado usando atoi() en C?

main() 
{ 
    char s[] = "AAA"; 
    advanceString(s); 
} 

void advanceString(p[3]) 
{ 
    int val = atoi(p); 
    printf("The atoi val is %d\n",val); 
} 

Aquí el valor atoi se muestra como 0, pero no pude averiguar la razón exacta. Según mi comprensión, ¿debería ser la suma del equivalente decimal de cada valor en la matriz? Por favor, corríjame si estoy equivocado.

+3

Pegue el código real que se compila. –

+2

@Marcelo ¿Eso realmente importa en este caso? –

+2

Tal vez, tal vez no. Pero, ¿es realmente tan difícil, dado que OP ya ha visto el resultado de ejecutar el programa? –

Respuesta

53

atoi() convierte una representación de cadena de un número entero en su valor. No convertirá caracteres arbitrarios en su valor decimal. Por ejemplo:

int main(void) 
{ 
    const char *string="12345"; 

    printf("The value of %s is %d\n", string, atoi(string)); 

    return 0; 
} 

No hay nada en la biblioteca estándar de C que convertirá "A" a 65 o "Z" a 90, que había necesidad de escribir que usted mismo, específicamente por cualquier juego de caracteres que está esperando como entrada.

Ahora que ya sabe lo que hace atoi(), favor no lo use para hacer frente a la entrada numérica en todo lo que ocurra. Realmente deberías lidiar con los aportes que no son lo que esperas. Hmm, ¿qué sucede cuando ingreso 65 en lugar de A? A los maestros les encanta romper cosas.

atoi() no hace ningún error en la comprobación de ningún tipo, lo que hace que todo lo que dependa de él para convertir una entrada arbitraria sea frágil, como mucho. En su lugar, utilice strtol() (ejemplo centrada en POSIX):

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

int main(void) 
{ 
    static const char *input ="123abc"; 
    char *garbage = NULL; 
    long value = 0; 

    errno = 0; 

    value = strtol(input, &garbage, 0); 

    switch (errno) { 
     case ERANGE: 
      printf("The data could not be represented.\n"); 
      return 1; 
     // host-specific (GNU/Linux in my case) 
     case EINVAL: 
      printf("Unsupported base/radix.\n"); 
      return 1; 
    } 

    printf("The value is %ld, leftover garbage in the string is %s\n", 
      // Again, host-specific, avoid trying to print NULL. 
      value, garbage == NULL ? "N/A" : garbage); 

    return 0; 
} 

cuando es ejecutado, esto da:

El valor es 123, la basura sobrante en la cadena es abc

Si no me importa guardar/examinar la basura, puede establecer el segundo argumento en NULL. No es necesario que free(garbage). También tenga en cuenta que si pasa 0 como tercer argumento, se supone que la entrada es el valor deseado de una representación decimal, hexadecimal o octal. Si necesita una raíz de 10, use 10 - fallará si la entrada no es la esperada.

También verificaría el valor de retorno para el valor máximo y mínimo que puede manejar un long int. Sin embargo, si se devuelve alguno para indicar un error, se establece errno. Un ejercicio para el lector es cambiar *input de 123abc a abc123.

Es importante comprobar la devolución, ya que su ejemplo muestra lo que sucede si no lo hace. AbcDeFg no es una representación de cadena de un entero, y debe tratar eso en su función.

Para su aplicación, el consejo más básico que puedo dar que sería una serie de interruptores, algo así como:

// signed, since a return value of 0 is acceptable (NULL), -1 
// means failure 
int ascii_to_ascii_val(const char *in) 
{ 
    switch(in) { 
     // 64 other cases before 'A' 
     case 'A': 
      return 65; 
     // keep going from here 
     default: 
      return -1; // failure 

} 

.. entonces que basta con ejecutar en un bucle.

O rellene previamente un diccionario que una función de búsqueda podría abarcar (mejor). No necesitaría hashes, solo un almacén de claves -> value, ya que sabe lo que va a contener de antemano, donde los caracteres ASCII estándar son claves, y sus identificadores correspondientes son valores.

+1

Puede usar [strerror] (http://en.wikipedia.org/wiki/Strerror) para obtener un texto legible para humanos a partir de un código de error. – Tarantula

+0

si bien la respuesta es correcta, sería más útil si la respuesta le dice al OP sobre cómo resolver su problema. – Alex

+0

@Alex Créalo o no, acabo de ver su comentario. Editado un poco :) –

6

Lea atoi() como ai (ASCII a entero).

atoi() convierte una cadena que representa un número decimal, a entero.

char s[] = "42"; 
int num = atoi(s); //The value of num is 42. 
+2

+1 porque contiene todo lo que necesita una respuesta ;-) – Frunsi

2

atoi espera que su argumento sea una representación de cadena de un decimal (base-10) entero constante; AAA no es una constante de entero decimal válida, por lo que atoi devuelve 0 porque no tiene otra forma de indicar que la entrada no es válida.

Tenga en cuenta que atoi convertirá hasta el primer carácter que no es parte de una constante entera válida; en otras palabras, "123" y "123w" se convertirán a 123.

Como todos los demás dicen, no use atoi; use strtol en su lugar.

Cuestiones relacionadas