字符指针类型之间的转换

Casting between character pointer types

给出

unsigned long long int strtoull (const char* str, char** endptr, int base);

这有效吗?

unsigned char *s = "123";
unsigned char *t;
unsigned long long n = strtoull(s, &t, 0);

将指向一种类型的指针转​​换为指向另一种类型的指针并使用它通常是不行的,严格来说 charunsigned char 是不同的类型,所以虽然有从某种意义上说,上面的代码显然是合理的,我想通过标准的严格字母来确保它不是未定义的行为。

类型不存在别名问题,原因有二。 C 2018 6.5 7 中的别名规则允许访问具有字符类型的对象(并且 unsigned charchar 都是字符类型),并且它们还允许访问具有符号或类型的对象与对象的有效类型相对应的无符号类型(您正在使用 unsigned charchar)。

但是,使用 unsigned char * 作为 const char * 参数的参数违反了兼容类型的规则,使用 unsigned char ** 参数作为 char ** 范围。第一种情况,如果插入对参数类型的显式强制转换,则转换由 C 2018 6.3.2.3 7 定义:“当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节。”

在第二种情况下,我没有看到 C 标准定义了一种将 unsigned char ** 转换为 char ** 并具有定义结果的方法。 (转换本身是允许的,但是 C 标准没有指定结果的值,除非它在转换回 unsigned char ** 时产生与原始指针等效的东西。)

如果编译器因将 char** 转换为 unsigned char** 而破坏您的程序的可能性是您真正关心的问题,则您可以解决类型差异问题。

#include <stdlib.h>

unsigned long long foo(const unsigned char* const p)
{
  const char* const s = (const char*)p;
  char* t = NULL;
  const unsigned long long n = strtoull(s, &t, 0);

  return n;
}

在这个简单的例子中,我可能会省略 s 的声明,只在需要的地方使用强制转换,而声明 t 根本没有任何意义。如果您经常使用别名并且它们可以节省击键次数,那么将它们声明为别名会更有意义。 (此外,对于字符类型和 void* 以外的类型,您可能希望将别名设为函数参数,这样它就不会出现在与原始类型相同的范围内,从而违反严格的别名规则。)

如果您需要传递 end_ptr 的值而不是 NULL,则可能您正在使用存储的值继续解析。要么你将它传递给另一个函数,比如 strtoull(),它需要一个 char*,或者你可以安全地转换为 unsigned char*,一个指向兼容类型的指针之间的转换别名规则。这引发了 (char**)&uchar_ptr 个问题中的 none 个。

代码实际上会在没有任何显式转换的情况下编译,但转换更清楚地表明别名是有意的(并抑制了编译器警告)。