Lesson 17 · 17 of 20
Errors: return codes and errno · C
Handle errors in C with your own return codes, errno, strtol and strerror: validating order quantities and reporting a missing file.

C has no exceptions: each function reports a failure through what it returns. The clearest approach is an enum of return codes, like Status, with the result left in an output parameter: parse_quantity returns OK or an error, and only when everything is fine does it write the quantity to *out, which is why qty stays at 0 for the invalid inputs in the output. status_text turns each code into a fixed message.
`strtol` turns text into a long and leaves in end a pointer to the first character it could not read. If end == text there was no number at all (abc); if *end is not '\0', something is left over (12kg). It is far safer than atoi, which returns 0 for abc and says nothing.
`errno`, from errno.h, is a global variable where the standard library records the last error. It is only written when something fails, so you set it to 0 before calling strtol and check it straight after: ERANGE means the number does not fit in a long, like 99999999999999999999.
fopen also leaves the reason in errno when it returns NULL, and `strerror(errno)` turns it into text: "No such file or directory". `perror("text")` does the same in one call, but writes to the error stream (stderr).
main ends with EXIT_SUCCESS; a program that fails returns EXIT_FAILURE, which is what the scripts that run it see. In DMarlon Studio the Console target shows the output when you run with F9.
Code · 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;}Output · 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
Key points
- Return a status code and leave the result behind a pointer.
strtolwithendcatches empty or leftover text.- Set
errno = 0before the call and check it right after. strerror(errno)andperrorexplain the system error.
Exercise
Add "-3" to the inputs and an ERR_NEGATIVE code so that it gives "negative quantity" instead of "out of range", and make main return EXIT_FAILURE if any input is invalid.