为什么endptr参数是strtof和strtod一个指向非const char指针的指针?

Dan*_*ien 22 c const-correctness std

标准C库函数strtofstrtod具有以下签名:

float strtof(const char *str, char **endptr);
double strtod(const char *str, char **endptr); 
Run Code Online (Sandbox Code Playgroud)

它们将输入字符串分解str为三部分:

  1. 空白的初始,可能是空的序列
  2. 表示浮点值的字符的"主题序列"
  3. 无法识别(并且不影响转换)的字符的"尾随序列".

如果endptr不是NULL,则*endptr设置为指向紧跟在转换的一部分的最后一个字符之后的字符的指针(换句话说,尾随序列的开始).

我想知道:endptr那么,为什么指向const char指针?不是*endptr指向const char字符串的指针(输入字符串str)?

R..*_*R.. 11

原因很简单.char *可以自动转换到const char *,但char **不能自动转换为const char **,并通过调用函数使用的指针(其地址被传递)的实际类型更可能是char *const char *.这种自动转换不可能的原因是,有一种非显而易见的方法可以const通过几个步骤来删除资格,其中每个步骤看起来完全有效并且本身是正确的.Steve Jessop在评论中提供了一个例子:

如果你可以自动转换char**const char**,那么你可以做到

char *p;
char **pp = &p;
const char** cp = pp;
*cp = (const char*) "hello";
*p = 'j';.
Run Code Online (Sandbox Code Playgroud)

对于const-safety,其中一条线必须是非法的,并且由于其他线都是完全正常的操作,因此必须如此 cp = pp;

一种更好的方法是定义这些函数来void *代替char **.双方char **const char **可以自动转换到void *.(被击中后的文本实际上是一个非常糟糕的主意,它不仅能防止任何类型检查,但是C实际上禁止类型的对象char *const char *别名).此外,这些功能可能已经采取了ptrdiff_t *size_t *在哪个参数存储偏移的结束,而不是指向它的指针.无论如何,这通常更有用.

如果您喜欢后一种方法,请随意在标准库函数周围编写这样的包装器并调用您的包装器,以便保留其余的代码 - const清理和无转换.

  • "`char**`和`const char**`都可以自动转换为void*." 我看到你在说什么,并且使用`void*`将允许用户在没有强制转换的情况下传入`const char**`.但它也会让他们在没有演员阵容的情况下传递一整套错误的东西.鉴于C的局限性,我认为最好保持基本的类型安全性,甚至是失去const安全性的成本. (2认同)
  • "如果有人有,请发帖." - 如果你可以自动将`char**'转换为`const char**`,那么你可以做`char*p; char**pp =&p; const char**cp = pp;*cp =(const char*)"你好";*p ='j';`.对于const-safety,其中一行必须是非法的,并且由于其他行都是完全正常的操作,它必须是`cp = pp;`. (2认同)

Aid*_*lly 5

可用性.该str说法被标记为const,因为输入参数将不会被修改.如果endptrconst,则会告诉调用者他不应该更改从endptr输出引用的数据,但通常调用者想要这样做.例如,我可能希望在获取浮点数后终止字符串:

float StrToFAndTerminate(char *Text) {
    float Num;

    Num = strtof(Text, &Text);
    *Text = '\0';
    return Num;
}
Run Code Online (Sandbox Code Playgroud)

在某些情况下,想做​​的事情是完全合理的.如果endptr属于类型则不起作用const char **.

理想情况下,endptr应该是与实际输入常量匹配的常量str,但C无法通过其语法指示这一点.(Anders Hejlsberg在描述为什么const被排除在C#之外时会谈到这一点.)