如何在const-correctness下实现strtol?

fgw7neuy  于 11个月前  发布在  其他
关注(0)|答案(4)|浏览(104)

根据http://www.cplusplus.com/reference/cstdlib/strtol/,此函数的签名为long int strtol (const char* str, char** endptr, int base)
但我想知道:如果它在字符串的开头传递了一个const char *,它如何在不作弊的情况下将其转换为指向第一个未处理字符的非const指针?不执行const_cast的strtol实现会是什么样子?

0yg35tkg

0yg35tkg1#

在const-correctness下如何实现strtol

你没有,因为strtol的定义本质上不是const-正确的。
这是C标准库中的一个缺陷。
有几个标准函数接受一个const char*参数(预期指向字符数组的开头),并给予一个非constchar*指针,可用于修改该数组。
strchr就是一个例子:

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

字符串
举例来说:

#include <string.h>
int main(void) {
    const char *s = "hello";
    char *ptr = strchr(s, 'h');
    *ptr = 'H';
}


这个程序有未定义的行为。在我的系统上,它由于分段错误而死亡。
strchr本身并没有出现这个问题,它承诺不会修改你传递给它的字符串,它确实没有修改,但是它返回了一个指针,调用者可以用这个指针来修改它。
早在20世纪80年代后期,ANSI C委员会就将每个这样的函数分成两个版本,一个作用于const字符数组,另一个作用于非const数组:

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


但这会破坏在const出现之前编写的ANSI之前的代码,这和C没有将字符串字面量设为const的原因是一样的。
C继承了C的大部分标准库,通过提供一些函数的重载版本来处理这个问题。
底线是,作为一名C程序员,你有责任不修改你定义为const的对象。在大多数情况下,语言会帮助你强制执行这一点,但并不总是如此。
至于这些函数如何设法返回一个指向const数据的非const指针,它们可能只是在内部使用了一个强制转换(而不是const_cast,它只存在于C
中)。
最新消息:即将到来的C23标准,改变了几个字符串函数(memchrstrchrstrpbrkstrrchrstrstr)到通用函数来解决这个问题,但截至N3054草案,这种变化还没有应用于strtol等。我会联系标准的编辑,但要改变这一点可能为时已晚

bqucvtff

bqucvtff2#

很有可能它只是使用铸造。
在标准库中有许多函数都有相同的属性,牺牲类型安全而不是简单性可能是原因,因为你不能像C++那样重载函数。
他们希望程序员承担责任,如果str是一个字符串文字,就不编辑endptr
由于其有限的类型系统,C是实用的人的实用语言。

fwzugrvs

fwzugrvs3#

strtol确实执行了const_cast(或等效的)。将const强制转换不是问题,使用结果指针来修改原始的-const指针可能会有问题。
但是strtol只是将这个指针返回给你而没有篡改它,所以一切都很好。

k5ifujac

k5ifujac4#

如何在const-correctness下实现strtol?
使用C11 _Generic将允许代码调用

// when passed argument for `str` is `char *` and for `endptr` is `char **`
long strotol(const char* str, char** endptr, int base);
// or
// when passed argument for `str` is `const char *` and for `endptr` is `const char **`
long strotol_c(const char* str, const char** endptr, int base);
// and warn/error otherwise

字符串
下面的实现是相同的,因为只需要保留函数签名。因为这与strtol()不同,它应该被称为其他东西,如strtol_s()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

long int strtol_c(const char * restrict nptr, const char ** restrict endptr, int base) {
  return strtol((char *) nptr, (char **) endptr, base);
}

#define strtol_s(n,e,b) _Generic(n, \
  char *: strtol((n), (e), (b)), \
  const char *: strtol_c((n), (e), (b)), \
  default: 0 \
  )

int main(void) {
  char *src = malloc(100);
  strcpy(src, "456");
  const char *srcc = "123";
  char *endptr;
  const char *endcptr;
  long L[6] = { 0 };

  // OK - matching str and *endptr
  L[0] = strtol_s(src,  &endptr, 0);

  // warning: passing argument 2 of 'strtol' from incompatible pointer type
  L[1] = strtol_s(src,  &endcptr, 0);

  // warning: passing argument 2 of 'strtol_c' from incompatible pointer type
  L[2] = strtol_s(srcc, &endptr, 0);

  // OK - matching str and *endptr
  L[3] = strtol_s(srcc, &endcptr, 0);

  L[4] = strtol(src, &endptr, 0);
  // warning passing argument 2 of 'strtol' from incompatible pointer type

  // OK
  L[5] = strtol(src, &endcptr, 0);
  return !L[0];
}


丢失的内容:strtol_s()不是一个真正的函数,因此无法创建指向它的指针。
它是如何在不作弊的情况下将其转换为指向第一个未处理字符的非常量指针的?
strtol(),尽管它接受char **endptr作为第二个参数,但不会修改*endptr

相关问题