strtol,strtod不安全吗?

Are strtol, strtod unsafe?

提问人:user102008 提问时间:6/15/2009 更新时间:6/15/2009 访问量:6337

问:

似乎并有效地允许(并迫使)你抛弃字符串中的恒常性:strtol()strtod()

#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;
}

上面,我自己没有进行任何石膏。然而,基本上把我扔进了我,没有任何警告或任何东西。(事实上,它不允许你键入为 ,因此会强制对类型进行不安全的更改。这不是很危险吗?strtol()const char *char *barconst char *

常量 std c-strings const-char

评论


答:

1赞 Jonathan Leffler 6/15/2009 #1

第一个参数的“const char *”表示不会修改字符串。strtol()

你对返回的指针做什么是你的事。

是的,它可以被视为类型安全违规行为;C++可能会以不同的方式做事(尽管据我所知,ISO/IEC 14882:1998使用与C相同的签名进行定义)。<cstdlib>

评论

0赞 Steve Jessop 6/15/2009
C++ 确实使用与 C 相同的签名定义了 strtol(以及 cstdlib 中的其他所有内容),但不是 cstring 和 cwchar 中的所有内容。
14赞 Adam Rosenfield 6/15/2009 #2

我猜是因为替代方案更糟。假设将原型更改为添加:const

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

现在,假设我们要解析一个非常量字符串:

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

但是,当我们尝试编译此代码时会发生什么?编译器错误!这很不直观,但您不能隐式地将 a 转换为 .请参阅 C++ FAQ Lite 以获取原因的详细说明。从技术上讲,它在那里谈论的是 C++,但这些参数对 C 同样有效。在 C/C++ 中,只允许您在最高级别隐式地从“指向类型的指针”转换为“指向类型的指针”:您可以执行的转换是从 到 ,或者等效地从“指针到(指针)”到“指针(指针)”。char **const char **constchar **char * const *charconstchar

由于我猜解析非常量字符串比解析常量字符串的可能性要大得多,因此我继续假设不太可能的情况的不正确性比使常见情况成为编译器错误更可取。const

评论

6赞 Thanatos 3/30/2011
但是 C++ 不会阻止您重载函数:您可以拥有 :这修复了您的编译错误。事实上,该标准对其他此类函数(如 和 ,long int strtol(char *nptr, char **endptr, int base);long int strtol(const char *nptr, const char **endptr, int base);strchrstrstr
0赞 Jonathan Leffler 1/13/2016
您可以参考 C FAQ 网站 const char *p、char const *p 和 char * const p 有什么区别? 更具体地说,为什么我不能将 char ** 传递给需要 const char ** 的函数? 而不是 C++ FAQ,尽管我并不完全相信这些解释很容易理解。
7赞 Steve Jessop 6/15/2009 #3

是的,其他函数也有同样的“持续洗钱”问题(例如strchr,strstr等等)。

正是出于这个原因,C++ 添加了重载 (21.4:4):函数签名被替换为两个声明:strchr(const char*, int)

const char* strchr(const char* s, int c);
      char* strchr(      char* s, int c);

但是,当然,在 C 语言中,你不能同时拥有同名的 const 正确版本,所以你会得到 const-incorrect 的折衷。

C++ 没有提到 strtol 和 strtod 的类似重载,事实上我的编译器 (GCC) 也没有它们。我不知道为什么不:你不能隐式转换为(加上没有重载)这一事实解释了 C 的情况,但我不太明白 C++ 重载有什么问题:char**const char**

long strtol(const char*, const char**, int);

评论

2赞 Alex Cohn 3/3/2013
STLPORT 提供此重载(以及其他一些重载)。
1赞 Joshua 6/15/2009 #4

我有一个编译器,在 C++ 模式下编译时提供:

extern "C" {
long int strtol(const char *nptr, const char **endptr, int base);
long int strtol(char *nptr, char **endptr, int base);
}

显然,这两者都解析为相同的链接时间符号。

编辑:根据 C++ 标准,此标头不应编译。我猜编译器根本没有检查这一点。事实上,这些定义在系统头文件中确实是这样显示的。