valores valor significa sale quitar que porque ocultar multiplicar error ejemplos div como buscarv c return

valor - si error buscarv



¿Cuál es la mejor manera de devolver un error de una función cuando ya estoy devolviendo un valor? (7)

Escribí una función en C que convierte una cadena en un entero y devuelve el número entero. Cuando llamo a la función también quiero que me avise si la cadena no es un número válido. En el pasado, devolvía -1 cuando se produjo este error, porque no necesitaba convertir cadenas en números negativos. Pero ahora quiero convertir cadenas a números negativos, entonces, ¿cuál es la mejor manera de informar el error?

En caso de que no tuviera claro esto: no quiero que esta función informe el error al usuario, quiero que informe el error al código que llamó a la función. ("Informe" podría ser la palabra incorrecta para usar ...)

Aquí está el código:

s32 intval(const char *string) { bool negative = false; u32 current_char = 0; if (string[0] == ''-'') { negative = true; current_char = 1; } s32 num = 0; while (string[current_char]) { if (string[current_char] < ''0'' || string[current_char] > ''9'') { // Return an error here.. but how? } num *= 10; num += string[current_char] - ''0''; current_char++; } if (negative) { num = -num; } return num; }


Observe cómo la biblioteca estándar trata este problema:

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

Aquí, después de la llamada, el endptr apunta al primer personaje que no pudo ser analizado. Si endptr == str, entonces no se convirtieron los caracteres, y esto es un problema.


Bueno, la forma en que .NET maneja esto en Int32.TryParse es devolver el éxito / falla, y pasar el valor analizado de nuevo con un parámetro de paso por referencia. Lo mismo podría aplicarse en C:

int intval(const char *string, s32 *parsed) { *parsed = 0; // So that if we return an error, the value is well-defined // Normal code, returning error codes if necessary // ... *parsed = num; return SUCCESS; // Or whatever }


En general, prefiero la forma en que Jon Skeet propuso, es decir. devolviendo un bool (int u uint) sobre el éxito y almacenando el resultado en una dirección pasada. Pero su función es muy similar a strtol, por lo que creo que es una buena idea usar la misma API (o similar) para su función. Si le da un nombre similar como my_strtos32, esto hace que sea fácil entender lo que hace la función sin leer la documentación.

EDITAR: Dado que su función está explícitamente basada en 10, my_strtos32_base10 es un nombre mejor. Siempre que su función no sea un cuello de botella, puede omitir su implementación. Y simplemente envuelve strtol:

s32 my_strtos32_base10(const char *nptr, char **endptr) { long ret; ret = strtol(nptr, endptr, 10); return ret; }

Si luego se da cuenta de que es un cuello de botella, puede optimizarlo para sus necesidades.


La variable errno global al estilo del os también es popular. Use errno.h .

Si errno no es cero, algo salió mal.

Aquí hay una referencia de página man para errno .


Puede devolver una instancia de una clase donde una propiedad sería el valor interesado, otra propiedad sería una bandera de estado de algún tipo. O bien, pase una instancia de la clase de resultado.

Pseudo code MyErrStatEnum = (myUndefined, myOK, myNegativeVal, myWhatever) ResultClass Value:Integer; ErrorStatus:MyErrStatEnum

Ejemplo 1:

result := yourMethod(inputString) if Result.ErrorStatus = myOK then use Result.Value else do something with Result.ErrorStatus free result

Ejemplo 2

create result yourMethod(inputString, result) if Result.ErrorStatus = myOK then use Result.Value else do something with Result.ErrorStatus free result

El beneficio de este enfoque es que puede expandir la información que regresa en cualquier momento agregando propiedades adicionales a la clase Result.

Para ampliar aún más este concepto, también se aplica a las llamadas a métodos con múltiples parámetros de entrada. Por ejemplo, en lugar de CallYourMethod (val1, val2, val3, bool1, bool2, string1) en su lugar, tenga una clase con propiedades que coincidan con val1, val2, val3, bool1, bool2, string1 y utilícelo como un único parámetro de entrada. Limpia las llamadas al método y hace que el código se modifique más fácilmente en el futuro. Estoy seguro de que has visto que las llamadas a métodos con más de unos pocos parámetros son mucho más difíciles de usar / depurar. (7 es lo más absoluto que diría)


una forma común es pasar un puntero a una bandera de éxito como esta:

int my_function(int *ok) { /* whatever */ if(ok) { *ok = success; } return ret_val; }

llámalo así:

int ok; int ret = my_function(&ok); if(ok) { /* use ret safely here */ }

EDITAR: implementación de ejemplo aquí:

s32 intval(const char *string, int *ok) { bool negative = false; u32 current_char = 0; if (string[0] == ''-'') { negative = true; current_char = 1; } s32 num = 0; while (string[current_char]) { if (string[current_char] < ''0'' || string[current_char] > ''9'') { // Return an error here.. but how? if(ok) { *ok = 0; } } num *= 10; num += string[current_char] - ''0''; current_char++; } if (negative) { num = -num; } if(ok) { *ok = 1; } return num; } int ok; s32 val = intval("123a", &ok); if(ok) { printf("conversion successful/n"); }


Hay varias formas. Todos tienen sus ventajas y desventajas.

  • Haga que la función devuelva un código de error y pase un puntero a una ubicación para devolver el resultado. Lo bueno de esto es que no hay sobrecarga del resultado. Lo malo es que no puedes usar el resultado real de la función directamente en una expresión.

    Evan Teran sugirió una variación de esto que hace que la persona que llama pase un puntero a una variable de éxito (que puede ser opcionalmente NULL si a la persona que llama no le importa) y devuelve el valor real de la función. Esto tiene la ventaja de permitir que la función se use directamente en expresiones cuando la persona que llama está OK con un valor predeterminado en un resultado de error o sabe que la función no puede fallar.

  • Utilice un valor de retorno "centinela" especial para indicar un error, como un número negativo (si los valores de retorno normales no pueden ser negativos) o INT_MAX o INT_MIN si los valores buenos no pueden ser tan extremos. A veces, para obtener información de error más detallada, debe consultarse una llamada a otra función (como GetLastError() ) o una variable global (como errno ). Esto no funciona bien cuando su valor devuelto no tiene valores no válidos, y muchas personas lo consideran una mala forma en general.

    Una función de ejemplo que utiliza esta técnica es getc (), que devuelve EOF si se llega al final del archivo o si se encuentra un error.

  • Haga que la función nunca devuelva una indicación de error directamente, sino que requiera que la persona que llama busque otra función o sea global. Esto es similar a cómo funciona el modo " On Error Goto Next " de VB, y se considera universalmente como un mal camino a seguir.

  • Sin embargo, otro camino a seguir es tener un valor ''predeterminado''. Por ejemplo, la función atoi() , que tiene prácticamente la misma funcionalidad que su función intval() , devolverá 0 cuando no puede convertir ningún carácter (es diferente de su función en que consume caracteres para convertir hasta que alcanza el final de la cadena o un personaje que no es un dígito).

    El inconveniente obvio aquí es que puede ser difícil decir si se ha convertido un valor real o si se ha pasado basura a atoi() .

    No soy un gran admirador de esta manera para manejar errores.

Actualizaré mientras otras opciones cruzan mi mente ...