你如何在const-correctness下实现strtol?

时间:2016-01-13 12:50:54

标签: c

根据http://www.cplusplus.com/reference/cstdlib/strtol/,此功能的签名为a

我想知道:如果它被传递给long int strtol (const char* str, char** endptr, int base)到字符串的开头,它如何设法将其变成一个非const指针指向第一个未经处理的字符而不作弊?什么是strtol的实现看起来不会执行const_cast?

4 个答案:

答案 0 :(得分:5)

  

如何在const-correctness下实现strtol

你没有,因为strtol的定义本质上不是const - 正确。

这是C标准库中的一个缺陷。

有几个标准函数采用const char*参数(预期指向字符数组的开头)并返回一个非const char*指针,可用于修改那个数组。

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本身。它承诺不会修改传递给它的字符串,但它不会。但它返回一个指针,然后调用者可以使用它来修改它。

ANSI C委员会,在20世纪80年代后期,可以将每个这样的函数分成两个版本,一个作用于const字符数组,另一个作用于非{{1数组:

const

但这会破坏在char *strchr(char *s, int c); const char *strcchr(const char *s, int c); 存在之前编写的现有ANSI前代码。这与C没有创建字符串文字const的原因相同。

继承C大部分标准库的C ++通过提供某些函数的重载版本来解决这个问题。

底线是,作为C程序员,负责不修改你定义为const的对象。在大多数情况下,该语言可以帮助您强制执行此操作,但并非总是如此。

至于这些函数如何设法返回非const指向const数据的指针,它们可能只是在内部使用强制转换(不是const,只存在于C ++中) 。这是假设它们是用C实现的,这可能但不是必需的。

答案 1 :(得分:2)

最有可能只是使用铸造。

有许多函数在标准库中具有相同的属性。牺牲类型安全性而不是简单性是可能的原因,因为你不能像在C ++中那样重载函数。

他们希望程序员负责,如果endptr是{1}},则不会编辑str

凭借它的有限类型系统,C是实用人士的实用语言。

答案 2 :(得分:1)

strtol确实做了const_cast(或等效)。转换const不是问题,使用结果指针修改原来的const指针可能是。

但是strtol只是将这个指针返回给你而不会篡改它,所以一切都很好。

答案 3 :(得分:1)

  

如何在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()不是真正的函数,因此无法指向它。

  

如何将其转换为指向第一个未处理角色的非const指针而不作弊?

strtol(),虽然它需要char **endptr作为第二个参数,但不会修改*endptr