【问题标题】:Are strtol, strtod unsafe?strtol、strtod 不安全吗?
【发布时间】: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 *,因此会强制进行不安全的类型更改。)这不是很危险吗?

【问题讨论】:

    标签: c constants std c-strings 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 对于不太可能的情况比使常见情况成为编译器错误更可取.

    【讨论】:

    【解决方案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++ 标准,此标头不应编译。我猜编译器根本没有检查这个。实际上,这些定义确实在系统头文件中显示为这样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-09-25
      • 2012-12-13
      • 1970-01-01
      • 2010-09-18
      • 2016-06-04
      • 2020-03-05
      • 1970-01-01
      • 2012-03-31
      相关资源
      最近更新 更多