2011-10-24 21 views
6

¿Podría alguien explicar por qué esas llamadas no están devolviendo el mismo resultado esperado?Diferentes resultados usando atoi

unsigned int GetDigit(const string& s, unsigned int pos) 
{ 
     // Works as intended 
     char c = s[pos]; 
     return atoi(&c); 

     // doesn't give expected results 
     return atoi(&s[pos]); 
     return atoi(&static_cast<char>(s[pos])); 
     return atoi(&char(s[pos])); 
} 

Observación: No estoy buscando la mejor manera de convertir una char a un int.

+0

El resultado "funciona según lo previsto" da como resultado UB, ya que está pasando a 'atoi' un único' char' en lugar de la cadena terminada en nulo que está esperando. –

+0

@littleadv: seguro, quise decir un * puntero * a un solo 'char'; y pasar un puntero a un solo carácter es seguramente UB, porque no tienes garantías de lo que lo sigue en la pila (en realidad, es UB sin ninguna duda porque estás haciendo que la memoria de acceso 'atoi' pase al último elemento de la" matriz " "). –

+1

@Matteo: sin ninguna duda, a menos que 's [pos]' pase a ser 0 byte, o un carácter que haga que 'atoi' deje de leer ;-) –

Respuesta

10

Ninguno de sus intentos es correcto, incluido el que dice "funciona según lo previsto" (simplemente funcionó por accidente). Para empezar, atoi() requiere una cadena terminada en NUL, que no está proporcionando.

¿Qué hay de lo siguiente:

unsigned int GetDigit(const string& s, unsigned int pos) 
{ 
     return s[pos] - '0'; 
} 

Esto supone que usted sabe que s[pos] es un dígito decimal válido. Si no lo hace, algunas verificaciones de errores están en orden.

+0

' atoi' deja de leer la cadena de entrada en el primer carácter que no puede reconocer como parte de un número. Este puede ser el carácter nulo. Entonces, en realidad parece que el carácter nulo no es definitivamente necesario, ¿no es así? Pero, sin embargo, tienes razón, ninguna de mis soluciones es correcta. –

+0

@RonaldMcBean: el carácter de terminación, cualquiera que sea, debe ser parte de la cadena, ya que leer más allá del final de la cadena es un comportamiento indefinido. – NPE

0

si desea acceder a los datos como una cadena C - use s.c_str(), y luego páselo a atoi.

atoi espera una cadena de estilo C, std::string es una clase de C++ con comportamientos y características diferentes. Para empezar, no tiene que ser terminado NULL.

0

atoi toma el puntero a char por su argumento. En el primer intento, cuando usa char c, toma el puntero a un solo carácter y, por lo tanto, obtiene la respuesta que desea. Sin embargo, en los otros intentos, lo que obtienes es un puntero a char que ha comenzado a ser una cadena de char s, por lo tanto, supongo que lo que obtienes después de atoi en los intentos posteriores es un número convertido de los caracteres en las posiciones pos, pos+1, pos+2 y hasta el final de la cadena s.

1

Dado que int atoi(const char* s) acepta un puntero a un campo de caracteres, los últimos tres usos devuelven un número correspondiente a los dígitos consecutivos que comienzan con & s [pos], p. puede dar 123 para una cadena como "123", comenzando en la posición 0. Dado que los datos dentro de un std::string no son requeridos para ser terminados en nulo, la respuesta puede ser cualquier otra en alguna implementación, es decir, un comportamiento indefinido.

Su enfoque de "trabajo" también utiliza un comportamiento indefinido. Es diferente de los otros intentos ya que copia el valor de s[pos] a otra ubicación. Parece funcionar solo mientras el byte adyacente en la memoria al lado del carácter c accidentalmente sea cero o un carácter que no sea un dígito, lo cual no está garantizado. Así que sigue los consejos dados por @aix.

para que funcione de verdad que podría hacer lo siguiente:

char c[2] = { s[pos], '\0' }; 
return atoi(c); 
0

Si realmente desea convertir un solo caracter en la cadena en la posición (en contraposición a una subcadena a partir de esa posición y terminando al final de la cadena), puede hacerlo de esta manera:

int GetDigit(const string& s, const size_t& pos) { 
    return atoi(string(1, s[pos]).c_str()); 
} 

int GetDigit2(const string& s, const size_t& pos) { 
    const char n[2] = {s[pos], '\0'}; 
    return atoi(n); 
} 

por ejemplo.

3

Lo que está haciendo es utilizar un std::string, llegar a un personaje de su representación interna y alimentar a un puntero a ella en atoi, que espera un const char* que apunta a una cadena terminada en cero. No se garantiza que un std::string almacene caracteres para que haya un cero de finalización, es solo suerte que su implementación en C++ parezca hacer esto.

La forma correcta sería solicitar std::string para una versión de finalización de cero de su contenido usando s.c_str(), luego llame al atoi usando un puntero al mismo.

Su código contiene otro problema, que está lanzando el resultado de atoi a un unsigned int, mientras atoi devuelve un int firmado. ¿Qué pasa si su cadena es "-123"?

+0

+1: para señalar el otro problema y una buena explicación –

Cuestiones relacionadas