4

Is strtoull guaranteed to either return a valid number, or set errno (if no valid number could be parsed)?

In other words, is this

errno = 0;
n = strtoull(s, 0, 10);
if (errno) {
  perror(s);
  exit(1);
}
// do something with n

correct, or is some other check also required?

Casey
  • 10,297
  • 11
  • 59
  • 88
rwallace
  • 31,405
  • 40
  • 123
  • 242
  • 2
    Beware: the C standard doesn't say that `errno` shall be set if no conversion can be performed — see C11 [§7.22.1.4 The `strtol`, `strtoll`, `strtoul` and `strtoull` functions](http://port70.net/~nsz/c/c11/n1570.html#7.22.1.4). It is likely that implementations do set `errno` then, but it's not mandatory according to the C standard. (As I type, the question is tagged both C and C++.). I've not checked the C++ standard today to see whether that is true there too. See also [Correct usage of `strtol`](https://stackoverflow.com/q/14176123/15168l). According to the standard, more checks are needed. – Jonathan Leffler Feb 15 '21 at 22:33

1 Answers1

5

No. The C standard (§7.22.1.7 of at least drafts N1570 through N2454, so C11 through C18) only mentions setting errno in one place:

If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

There is another possible error condition, however. “If no conversion could be performed, zero is returned,” and “the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer.” Otherwise, a pointer to “the final string” would be stored there, which would compare greater to nptr.

Therefore, a truly standard-compliant C program should check both conditions. This test program, compiled with clang -std=c18 on Windows 10:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main (void) {
  const char s[] = "a1";

  errno = 0;
  char* unparsed = NULL;
  const unsigned long long int n =
    strtoull( s, &unparsed, 10 );
  
  if ( errno || (!n && s == unparsed) ) {
    fflush(stdout); // Don't cross the streams!
    perror(s);
    exit(EXIT_FAILURE);
  }
  
  printf( "%llu\n", n );
  return EXIT_SUCCESS;
}

produces the output a1: No error, so on this implementation, errno is not set.

As chqrlie brings up in the comments, it would have sufficed to check ( errno || s == unparsed ). If neither of these conditions hold, there was a conversion performed on a non-empty subject sequence.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • 2
    Checking the value of `n` seems redundant. If a number was found, `unparsed` will be `> s` to it suffices to write: `if ( errno || s == unparsed )` – chqrlie Feb 16 '21 at 00:38