Uso correcto de strtol

El siguiente progtwig convierte una cadena en larga, pero según mi comprensión, también devuelve un error. Estoy confiando en el hecho de que si strtol convirtió con éxito la cadena a long, entonces el segundo parámetro para strtol debería ser igual a NULL. Cuando ejecuto la aplicación siguiente con 55, obtengo el siguiente mensaje.

 ./convertToLong 55 Could not convert 55 to long and leftover string is: 55 as long is 55 

¿Cómo puedo detectar errores de strtol? En mi aplicación, cero es un valor válido.

Código:

 #include  #include  static long parseLong(const char * str); int main(int argc, char ** argv) { printf("%s as long is %ld\n", argv[1], parseLong(argv[1])); return 0; } static long parseLong(const char * str) { long _val = 0; char * temp; _val = strtol(str, &temp, 0); if(temp != '\0') printf("Could not convert %s to long and leftover string is: %s", str, temp); return _val; } 

Ya casi estás ahí. temp propia temp no será nula, pero apuntará a un carácter nulo si se convierte toda la cadena, por lo que debe desreferenciarla:

 if (*temp != '\0') 

Tenga en cuenta que los nombres que comienzan con un guión bajo están reservados para la implementación; es mejor evitar el uso de tales nombres en su código. Por lo tanto, _val debería ser solo val .

La especificación completa del manejo de errores para strtol() y sus parientes es compleja, sorprendentemente compleja, cuando se encuentra con ella por primera vez. Una cosa que está haciendo absolutamente bien es usar una función para invocar strtol() ; usarlo ‘en bruto’ en el código probablemente no sea correcto.

Dado que la pregunta está etiquetada con C y C ++, voy a citar el estándar C2011; puede encontrar la redacción adecuada en el estándar C ++ para usted.

ISO / CEI 9899: 2011 §7.22.1.4 Las funciones strtol , strtoll , strtoul y strtoull

long int strtol(const char * restrict nptr, char ** restrict endptr, int base);

¶2 […] Primero, descomponen la cadena de entrada en tres partes: una secuencia inicial, posiblemente vacía, de espacios en blanco (según lo especificado por la función isspace), una secuencia de sujeto que se asemeja a un entero representado en algún radix determinado por el valor de base, y una cadena final de uno o más caracteres no reconocidos, incluido el carácter nulo de terminación de la cadena de entrada. […]

¶7 Si la secuencia del asunto está vacía o no tiene la forma esperada, no se realiza ninguna conversión; el valor de nptr se almacena en el objeto apuntado por endptr , siempre que endptr no sea un puntero nulo.

Devoluciones

¶8 Las funciones strtol , strtoll , strtoul y strtoull devuelven el valor convertido, si lo hay. Si no se puede realizar ninguna conversión, se devuelve cero. Si el valor correcto está fuera del rango de valores representables, se devuelve LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX o ULLONG_MAX (de acuerdo con el tipo de devolución y el signo del valor, si corresponde), y el valor de la macro ERANGE es almacenado en errno .

Recuerde que ninguna función de biblioteca C estándar establece errno en 0. Por lo tanto, para ser confiable, debe establecer errno en cero antes de llamar a strtol() .

Entonces, su función parseLong() podría verse así:

 static long parseLong(const char *str) { errno = 0; char *temp; long val = strtol(str, &temp, 0); if (temp == str || *temp != '\0' || ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) fprintf(stderr, "Could not convert '%s' to long and leftover string is: '%s'\n", str, temp); // cerr << "Could not convert '" << str << "' to long and leftover string is '" // << temp << "'\n"; return val; } 

Tenga en cuenta que, en caso de error, esto devuelve 0 o LONG_MIN o LONG_MAX, dependiendo de qué strtol() devuelto. Si su código de llamada necesita saber si la conversión fue exitosa o no, necesita una interfaz de función diferente, consulte a continuación. Además, tenga en cuenta que los errores se deben imprimir en stderr lugar de stdout , y los mensajes de error deben terminar con una nueva línea \n ; si no lo son, no se garantiza que aparezcan de manera oportuna.

Ahora, en el código de la biblioteca probablemente no desee imprimir, y su código de llamada podría querer saber si la conversión fue exitosa o no, por lo que también podría revisar la interfaz. En ese caso, probablemente modificaría la función para que devuelva una indicación de éxito / falla:

 bool parseLong(const char *str, long *val) { char *temp; bool rc = true; errno = 0; *val = strtol(str, &temp, 0); if (temp == str || *temp != '\0' || ((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE)) rc = false; return rc; } 

que podrías usar como:

 if (parseLong(str, &value)) …conversion successful… else …handle error… 

Si necesita distinguir entre 'basura chatarra', 'cadena numérica inválida', 'valor demasiado grande' y 'valor demasiado pequeño' (y 'sin error'), usaría un entero o enum lugar de un código de retorno booleano . Si desea permitir el espacio en blanco al final pero no otros caracteres, o si no desea permitir ningún espacio en blanco inicial, tiene más trabajo por hacer en la función. El código permite octal, decimal y hexadecimal; si quieres estrictamente decimal, necesitas cambiar el 0 a 10 en la llamada a strtol() .

Si sus funciones se enmascaran como parte de la biblioteca estándar, no deberían establecer errno en 0 permanentemente, por lo que tendría que ajustar el código para conservar errno :

 int saved = errno; // At the start, before errno = 0; …rest of function… if (errno == 0) // Before the return errno = saved; 

Te estás perdiendo un nivel de indirección. Desea verificar si el carácter es el NUL terminación, y no si el puntero es NULL :

 if (*temp != '\0') 

Por cierto, este no es un buen enfoque para la comprobación de errores. El método adecuado de comprobación de errores de la familia de funciones strto* no se hace comparando el puntero de salida con el final de la cadena. Se debe hacer comprobando un valor de retorno cero y obteniendo el valor de retorno de errno .

Deberías estar revisando

 *temp != '\0' 

También debería poder verificar el valor de errno después de llamar a strotol de acuerdo con esto:

 RETURN VALUES The strtol(), strtoll(), strtoimax(), and strtoq() functions return the result of the conversion, unless the value would underflow or overflow. If no conver- sion could be performed, 0 is returned and the global variable errno is set to EINVAL (the last feature is not portable across all platforms). If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped according to the following table.