strtol、strtod 不安全吗?

Posted

技术标签:

【中文标题】strtol、strtod 不安全吗?【英文标题】:Are strtol, strtod unsafe? 【发布时间】:2010-11-02 21:11:18 【问题描述】:

似乎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 *,没有任何警告或任何东西。 (实际上,它不允许您将bar 输入为const char *,因此会强制进行不安全的类型更改。)这不是很危险吗?

【问题讨论】:

【参考方案1】:

我猜是因为替代方案更糟。假设原型改成添加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

但是当我们尝试编译这段代码时会发生什么?编译器错误!它相当不直观,但您不能将char ** 隐式转换为const char **。有关原因的详细说明,请参阅C++ FAQ Lite。从技术上讲,它在那里谈论 C++,但这些参数对 C 同样有效。在 C/C++ 中,您只能隐式地从“指向 type 的指针”转换为“指向 const 的指针” em>type" 在***别:您可以执行的转换是从char **char * const *,或者等效地从“pointer to (pointer to char)”到“pointer to (const指向char)"的指针。

由于我猜想解析非常量字符串的可能性远大于解析常量字符串,因此我会继续假设 const-incorrectness 对于不太可能的情况比使常见情况成为编译器错误更可取.

【讨论】:

但是 C++ 不会阻止你重载函数:你可以有long int strtol(char *nptr, char **endptr, int base); long int strtol(const char *nptr, const char **endptr, int base);:这修复了你的编译错误。事实上,该标准对其他此类功能也是如此,例如strchrstrstr 您可以参考 C 常见问题解答网站 What's the difference between const char *p, char const *p, and char * const p?,更具体地说,Why can't I pass a char ** to a function which expects a const char **? 而不是 C++ 常见问题解答,尽管我并不完全相信这些解释很容易理解。跨度> 【参考方案2】:

是的,其他函数也有同样的“const-laundering”问题(例如 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-correct 版本的同名,所以你会得到 const-incorrect 妥协。

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

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

【讨论】:

stlport 提供了这种重载(以及其他一些)。【参考方案3】:

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

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

是的,它可以被视为类型安全违规; C++ 可能会做不同的事情(不过,据我所知,ISO/IEC 14882:1998 定义了 &lt;cstdlib&gt; 与 C 中的签名相同)。

【讨论】:

C++ 确实定义了 strtol(以及 cstdlib 中的所有其他内容),其签名与 C 相同,但不是 cstring 和 cwchar 中的所有内容。【参考方案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++ 标准,此标头不应编译。我猜编译器根本没有检查这个。实际上,这些定义确实在系统头文件中显示为这样。

【讨论】:

以上是关于strtol、strtod 不安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

c语言strtod()函数的用法

将 Long 转换为 Unsigned Short Int / Strtol

Strtol 第二个参数

strtol 等人的规范中的混淆语言

strtol函数讲解

c - 如何为 strtol 设置限制