Search code examples
cconstantsstdc-stringsconst-char

Are strtol, strtod unsafe?


It seems that strtol() and strtod() effectively allow (and force) you to cast away constness in a string:

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

int main() {
  const char *foo = "Hello, world!";
  char *bar;
  strtol(foo, &bar, 10); // or strtod(foo, &bar);
  printf("%d\n", foo == bar); // prints "1"! they're equal
  *bar = 'X'; // segmentation fault
  return 0;
}

Above, I did not perform any casts myself. However, strtol() basically cast my const char * into a char * for me, without any warnings or anything. (In fact, it wouldn't allow you to type bar as a const char *, and so forces the unsafe change in type.) Isn't that really dangerous?


Solution

  • I would guess that because the alternative was worse. Suppose the prototype were changed to add const:

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

    Now, suppose we want to parse a non-constant string:

    char str[] = "12345xyz";  // non-const
    char *endptr;
    lont result = strtol(str, &endptr, 10);
    *endptr = '_';
    printf("%s\n", str);  // expected output: 12345_yz
    

    But what happens when we try to compile this code? A compiler error! It's rather non-intuitive, but you can't implicitly convert a char ** to a const char **. See the C++ FAQ Lite for a detailed explanation of why. It's technically talking about C++ there, but the arguments are equally valid for C. In C/C++, you're only allowed to implicitly convert from "pointer to type" to "pointer to const type" at the highest level: the conversion you can perform is from char ** to char * const *, or equivalently from "pointer to (pointer to char)" to "pointer to (const pointer to char)".

    Since I would guess that parsing a non-constant string is far more likely than parsing a constant string, I would go on to postulate that const-incorrectness for the unlikely case is preferable to making the common case a compiler error.