Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 674
user102008 Avatar asked Jun 14 '09 20:06

user102008


People also ask

How do I know if strtol failed?

To determine if 0 is valid, you must also look at the value errno was set do during the call (if it was set). Specifically, if errno != 0 and the value returned by strtol is 0 , then the value returned by strtol is INVALID.

Is Atoi safe?

* The atoi function is not thread-safe and also not async-cancel safe. * The atoi function has been deprecated by strtol and should not be used in new code.

Does strtol allocate memory?

Nowhere does the strtol doc say anything about allocating memory; it quite clearly says that the endptr (not error) is set to point to the char that wasn't parsed. And if it did allocate memory, you would be leaking it, as you only do the free outside the loop.

Is strtol fast?

strtol() (and its family) is the safest standard one and also a very fast one, but it is incredibly difficult to use, so I decided to write a safe and simple interface to it.


1 Answers

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.

like image 114
Adam Rosenfield Avatar answered Sep 26 '22 00:09

Adam Rosenfield