DMarlon Studio

Lección 17 · 17 de 20

Errores: códigos de retorno y errno · C

Gestione errores en C con códigos de retorno propios, errno, strtol y strerror: validar cantidades de un pedido y avisar si falta un archivo.

C · Errores: códigos de retorno y errno en DMarlon Studio: el código del ejemplo y su salida en la terminal

C no tiene excepciones: cada función avisa de un fallo con lo que devuelve. La forma más clara es un enum de códigos de retorno, como Status, y dejar el resultado en un parámetro de salida: parse_quantity devuelve OK o un error, y solo si todo va bien escribe la cantidad en *out; por eso, en la salida, qty sigue a 0 en las entradas con error. status_text traduce cada código a un mensaje fijo.

`strtol` convierte texto en long y, en end, deja un puntero al primer carácter que no ha podido leer. Si end == text no había ningún número (abc); si *end no es '\0', sobra algo detrás (12kg). Es mucho más seguro que atoi, que con abc devuelve 0 sin decir nada.

`errno`, de errno.h, es una variable global donde la biblioteca estándar anota el último error. Solo se escribe cuando algo falla, así que hay que ponerla a 0 antes de llamar a strtol y mirarla justo después: ERANGE significa que el número no cabe en un long, como 99999999999999999999.

fopen también deja la causa en errno cuando devuelve NULL, y `strerror(errno)` la convierte en texto: «No such file or directory». `perror("texto")` hace lo mismo en una sola llamada, pero escribe en la salida de errores (stderr).

main termina con EXIT_SUCCESS; un programa que falla devuelve EXIT_FAILURE, y es lo que ven los scripts que lo ejecutan. En DMarlon Studio el destino Consola muestra la salida al ejecutar con F9.

Código · main.c

#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>// Return codes: the function reports what happened; the result goes in *out.typedef enum { OK, ERR_EMPTY, ERR_NOT_NUMBER, ERR_RANGE } Status;const char *status_text(Status s) {    static const char *texts[] = {"ok", "empty text", "not a whole number", "out of range"};    return texts[s];}Status parse_quantity(const char *text, long *out) {    if (*text == '\0') return ERR_EMPTY;    char *end;    errno = 0;                         // strtol only sets errno when it fails    long value = strtol(text, &end, 10);    if (end == text || *end != '\0') return ERR_NOT_NUMBER;  // "12kg": end points at k    if (errno == ERANGE || value < 1 || value > 500) return ERR_RANGE;    *out = value;    return OK;}int main(void) {    const char *inputs[] = {"25", "", "12kg", "abc", "99999999999999999999", "0"};    for (int i = 0; i < 6; i++) {        long qty = 0;        Status st = parse_quantity(inputs[i], &qty);        printf("%-22s %s (%ld)\n", *inputs[i] ? inputs[i] : "(empty)", status_text(st), qty);    }    FILE *f = fopen("missing-orders.csv", "r");  // the standard library uses errno    if (f == NULL) printf("Cannot open missing-orders.csv: %s\n", strerror(errno));    else fclose(f);    return EXIT_SUCCESS;}

Salida · cc main.c -o main && ./main

25                     ok (25)
(empty)                empty text (0)
12kg                   not a whole number (0)
abc                    not a whole number (0)
99999999999999999999   out of range (0)
0                      out of range (0)
Cannot open missing-orders.csv: No such file or directory

Para recordar

  • Devuelva un código de estado y deje el resultado en un puntero.
  • strtol con end detecta texto vacío o sobrante.
  • Ponga errno = 0 antes de la llamada y mírelo justo después.
  • strerror(errno) y perror explican el error del sistema.

Ejercicio

Añada "-3" a las entradas y el código ERR_NEGATIVE para que esa entrada dé «negative quantity» en vez de «out of range», y haga que main devuelva EXIT_FAILURE si alguna entrada no es válida.